Exemplo n.º 1
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// This is the heart of CopyObject Pass 2.
        /// Sets the references recursively. Needs to go down recursively into
        /// all of the source object's owned properties looking for Reference
        /// properties, since those owned properties will have just been copied too.
        /// </summary>
        /// <param name="source">The source.</param>
        /// ------------------------------------------------------------------------------------
        private void SetReferencesRecursively(ICmObject source)
        {
            // Get the copy of our source object in which to set references.
            ICmObject newObj;
            int       hvoSrc = source.Hvo;

            if (!m_sourceToCopyMap.TryGetValue(hvoSrc, out newObj))
            {
                throw new ArgumentOutOfRangeException("source", "Can't find copy of original object in copy map.");
            }

            if (source is ICloneableCmObject)
            {
                return;                 // Assume that the object handled this itself
            }
            // TODO: Copy references from source object to copied object unless reference is to something in the sourceToCopyMap
            // in that case replace it with a reference to the copied version.
            // We'll need to go down inside of all owned objects to do this too, since they were just copied.
            int srcClsID = source.ClassID;
            int hvoCopy  = newObj.Hvo;

            int[] srcFlids = GetAllFieldsFromClassId(srcClsID);

            // Clone each owned flid and copy basic properties
            for (int i = 0; i < srcFlids.Length; i++)
            {
                int thisFlid = srcFlids[i];
                // If thisFlid is part of CmObject, this Flid of copied object has
                //   already been correctly set on creation.
                // Also skip virtual fields; let the object handle its own virtuals.
                if (thisFlid < 200 || m_mdc.get_IsVirtual(thisFlid))
                {
                    continue;
                }

                // Skip basic/string properties this pass
                int flidType = m_mdc.GetFieldType(thisFlid);
                if (flidType < (int)CellarPropertyType.MinObj)
                {
                    continue;
                }

                // Only have reference and owned props left
                if (m_cache.IsReferenceProperty(thisFlid))
                {
                    SetReferencesForReferenceFlid(thisFlid, flidType, hvoSrc, hvoCopy);
                }
                else
                {
                    SetReferencesForOwnedFlid(thisFlid, flidType, hvoSrc);
                }
            }
        }
Exemplo n.º 2
0
        private static Dictionary <string, List <PropertyInfo> > CacheBasicProperties(IFwMetaDataCacheManaged mdc)
        {
            var cachedBasicProperties = new Dictionary <string, List <PropertyInfo> >();

            foreach (var classId in mdc.GetClassIds())
            {
                var className = mdc.GetClassName(classId);
                List <PropertyInfo> basicProps;
                if (!cachedBasicProperties.TryGetValue(className, out basicProps))
                {
                    basicProps = new List <PropertyInfo>();
                    cachedBasicProperties.Add(className, basicProps);
                }
                basicProps.AddRange(mdc.GetFields(classId, className != "CmObject", (int)CellarPropertyTypeFilter.AllBasic).Select(propId => new PropertyInfo
                {
                    m_propertyName = mdc.GetFieldName(propId),
                    m_propertyType = (CellarPropertyType)mdc.GetFieldType(propId),
                    m_isCustom     = mdc.IsCustom(propId),
                    m_isVirtual    = mdc.get_IsVirtual(propId)
                }));
                if (basicProps.Count == 0)
                {
                    cachedBasicProperties.Remove(className);
                }
            }
            return(cachedBasicProperties);
        }
Exemplo n.º 3
0
 /// <summary>
 /// Gets the type of the field. This value indicates if the field is a primitive data type
 /// or a MultiStr/MultiTxt value or describes the relationship
 /// between two classes (i.e. owning/reference and atomic/collection/sequence). These
 /// numeric values are defined in the <b>~FWROOT\src\cellar\lib\CmTypes.h</b> file.
 ///</summary>
 /// <param name='luFlid'>Field identification number. In the database, this corresponds to the "Id"
 /// column in the Field$ table.
 /// Historical note: at one point, the result could include the virtual bit, kcptVirtual, or'd
 /// with one of the other kcpt values. This caused endless bugs and has been removed. </param>
 /// <returns>Points to the output field type.</returns>
 public virtual int GetFieldType(int luFlid)
 {
     return(m_metaDataCache.GetFieldType(luFlid));
 }
Exemplo n.º 4
0
        private void SetSubControl()
        {
            m_groupOptions.Controls.Clear();
            if (m_rsfm.m_flid == 0)
            {
                m_groupOptions.Text = LexTextControls.ksDiscardedField;
                m_groupOptions.Controls.Add(m_discardOpt);
                m_discardOpt.Location = m_locSubCtrl;
                return;
            }
            CellarPropertyType cpt = (CellarPropertyType)m_mdc.GetFieldType(m_rsfm.m_flid);
            int clidDst            = -1;

            switch (cpt)
            {
            case CellarPropertyType.ReferenceAtomic:
            case CellarPropertyType.ReferenceCollection:
            case CellarPropertyType.ReferenceSequence:
                clidDst = m_mdc.GetDstClsId(m_rsfm.m_flid);
                switch (clidDst)
                {
                case RnGenericRecTags.kClassId:
                    m_groupOptions.Text = LexTextControls.ksRnLinkFieldOptions;
                    m_groupOptions.Controls.Add(m_linkOpt);
                    m_linkOpt.Location = m_locSubCtrl;
                    break;

                case CrossReferenceTags.kClassId:
                case ReminderTags.kClassId:
                    throw new NotImplementedException(LexTextControls.ksUnimplementedField);

                default:
                    int clidBase = clidDst;
                    while (clidBase != 0 && clidBase != CmPossibilityTags.kClassId)
                    {
                        clidBase = m_mdc.GetBaseClsId(clidBase);
                    }
                    if (clidBase == CmPossibilityTags.kClassId)
                    {
                        m_groupOptions.Text = LexTextControls.ksListRefImportOptions;
                        m_groupOptions.Controls.Add(m_listOpt);
                        m_listOpt.Location = m_locSubCtrl;
                        m_listOpt.Initialize(m_cache, m_helpTopicProvider, m_app, m_stylesheet, m_rsfm, cpt);
                        break;
                    }
                    throw new ArgumentException(LexTextControls.ksInvalidField);
                }
                break;

            case CellarPropertyType.OwningAtomic:
            case CellarPropertyType.OwningCollection:
            case CellarPropertyType.OwningSequence:
                clidDst = m_mdc.GetDstClsId(m_rsfm.m_flid);
                switch (clidDst)
                {
                case StTextTags.kClassId:
                    Debug.Assert(cpt == CellarPropertyType.OwningAtomic);
                    m_groupOptions.Text = LexTextControls.ksTextImportOptions;
                    m_groupOptions.Controls.Add(m_textOpt);
                    m_textOpt.Location = m_locSubCtrl;
                    m_textOpt.Initialize(m_cache, m_helpTopicProvider, m_app, m_stylesheet, m_rsfm);
                    break;

                case RnRoledParticTags.kClassId:
                    m_groupOptions.Text = LexTextControls.ksListRefImportOptions;
                    m_groupOptions.Controls.Add(m_listOpt);
                    m_listOpt.Location = m_locSubCtrl;
                    m_listOpt.Initialize(m_cache, m_helpTopicProvider, m_app, m_stylesheet, m_rsfm, cpt);
                    break;

                case RnGenericRecTags.kClassId:
                    throw new NotImplementedException(LexTextControls.ksUnimplementedField);

                default:
                    throw new ArgumentException(LexTextControls.ksInvalidField);
                }
                break;

            case CellarPropertyType.MultiString:
            case CellarPropertyType.MultiUnicode:
                m_groupOptions.Text = LexTextControls.ksMultiStringImportOptions;
                m_groupOptions.Controls.Add(m_stringOpt);
                m_stringOpt.Location = m_locSubCtrl;
                m_stringOpt.Initialize(m_cache, m_helpTopicProvider, m_app, m_stylesheet, m_rsfm);
                break;

            case CellarPropertyType.String:
                m_groupOptions.Text = LexTextControls.ksStringImportOptions;
                m_groupOptions.Controls.Add(m_stringOpt);
                m_stringOpt.Location = m_locSubCtrl;
                m_stringOpt.Initialize(m_cache, m_helpTopicProvider, m_app, m_stylesheet, m_rsfm);
                break;

            case CellarPropertyType.GenDate:
                m_groupOptions.Text = LexTextControls.ksGenDateImportOptions;
                m_groupOptions.Controls.Add(m_dateOpt);
                m_dateOpt.Location = m_locSubCtrl;
                m_dateOpt.Initialize(m_cache, m_helpTopicProvider, m_rsfm, true);
                break;

            case CellarPropertyType.Time:
                m_groupOptions.Text = LexTextControls.ksDateTimeImportOptions;
                m_groupOptions.Controls.Add(m_dateOpt);
                m_dateOpt.Location = m_locSubCtrl;
                m_dateOpt.Initialize(m_cache, m_helpTopicProvider, m_rsfm, false);
                break;

            case CellarPropertyType.Unicode:
            case CellarPropertyType.Binary:
            case CellarPropertyType.Image:
            case CellarPropertyType.Boolean:
            case CellarPropertyType.Float:
            case CellarPropertyType.Guid:
            case CellarPropertyType.Integer:
            case CellarPropertyType.Numeric:
                throw new ArgumentException(LexTextControls.ksInvalidField);
            }
        }
Exemplo n.º 5
0
        private void ExportCustomFields(TextWriter writer, IRnGenericRec record)
        {
            ISilDataAccessManaged sda = m_cache.DomainDataByFlid as ISilDataAccessManaged;

            Debug.Assert(sda != null);
            foreach (int flid in m_customFlids)
            {
                string             fieldName = m_mdc.GetFieldName(flid);
                bool               fHandled  = false;
                ITsString          tss;
                string             s;
                CellarPropertyType cpt = (CellarPropertyType)m_mdc.GetFieldType(flid);
                switch (cpt)
                {
                case CellarPropertyType.Boolean:
                    break;

                case CellarPropertyType.Integer:
                    break;

                case CellarPropertyType.Numeric:
                    break;

                case CellarPropertyType.Float:
                    break;

                case CellarPropertyType.Time:
                    break;

                case CellarPropertyType.Guid:
                    break;

                case CellarPropertyType.Image:
                case CellarPropertyType.Binary:
                    break;

                case CellarPropertyType.GenDate:
                    break;

                case CellarPropertyType.String:
                    tss = sda.get_StringProp(record.Hvo, flid);
                    if (tss != null && tss.Text != null)
                    {
                        ExportString(writer, tss, fieldName);
                    }
                    fHandled = true;
                    break;

                case CellarPropertyType.MultiString:
                case CellarPropertyType.MultiUnicode:
                    ITsMultiString tms = sda.get_MultiStringProp(record.Hvo, flid);
                    int            cch = 0;
                    for (int i = 0; i < tms.StringCount; ++i)
                    {
                        int ws;
                        tss  = tms.GetStringFromIndex(i, out ws);
                        cch += tss.Length;
                        if (cch > 0)
                        {
                            break;
                        }
                    }
                    if (cch > 0)
                    {
                        writer.WriteLine("<Field name=\"{0}\" type=\"MultiString\">", fieldName);
                        for (int i = 0; i < tms.StringCount; ++i)
                        {
                            int ws;
                            tss = tms.GetStringFromIndex(i, out ws);
                            if (tss != null && tss.Length > 0)
                            {
                                if (cpt == CellarPropertyType.MultiString)
                                {
                                    writer.WriteLine(TsStringUtils.GetXmlRep(tss,
                                                                             m_cache.WritingSystemFactory, ws, true));
                                }
                                else
                                {
                                    writer.WriteLine("<AUni ws=\"{0}\">{1}</AUni>",
                                                     m_cache.WritingSystemFactory.GetStrFromWs(ws),
                                                     XmlUtils.MakeSafeXml(tss.Text));
                                }
                            }
                        }
                        writer.WriteLine("</Field>");
                    }
                    fHandled = true;
                    break;

                case CellarPropertyType.Unicode:
                    break;

                case CellarPropertyType.ReferenceAtomic:
                case CellarPropertyType.ReferenceCollection:
                case CellarPropertyType.ReferenceSequence:
                {
                    int        destClid  = m_mdc.GetDstClsId(flid);
                    List <int> rghvoDest = new List <int>();
                    if (cpt == CellarPropertyType.ReferenceAtomic)
                    {
                        int hvo = sda.get_ObjectProp(record.Hvo, flid);
                        if (hvo != 0)
                        {
                            if (destClid == CmPossibilityTags.kClassId)
                            {
                                ICmPossibility poss = PossibilityRepository.GetObject(hvo);
                                ExportAtomicReference(writer, poss, fieldName, "CmPossibility");
                                fHandled = true;
                            }
                            else
                            {
                                rghvoDest.Add(hvo);
                            }
                        }
                        else
                        {
                            fHandled = true;
                        }
                    }
                    else
                    {
                        int[] hvos = sda.VecProp(record.Hvo, flid);
                        if (hvos.Length > 0)
                        {
                            if (destClid == CmPossibilityTags.kClassId)
                            {
                                List <ICmPossibility> collection = new List <ICmPossibility>();
                                foreach (int hvo in hvos)
                                {
                                    collection.Add(PossibilityRepository.GetObject(hvo));
                                }
                                ExportReferenceList(writer, collection, fieldName, "CmPossibility", cpt);
                                fHandled = true;
                            }
                            else
                            {
                                rghvoDest.AddRange(hvos);
                            }
                        }
                        else
                        {
                            fHandled = true;
                        }
                    }
                    if (rghvoDest.Count > 0)
                    {
                    }
                }
                break;

                case CellarPropertyType.OwningAtomic:
                case CellarPropertyType.OwningCollection:
                case CellarPropertyType.OwningSequence:
                {
                    int        destClid  = m_mdc.GetDstClsId(flid);
                    List <int> rghvoDest = new List <int>();
                    if (cpt == CellarPropertyType.OwningAtomic)
                    {
                        int hvo = sda.get_ObjectProp(record.Hvo, flid);
                        if (hvo != 0)
                        {
                            if (destClid == StTextTags.kClassId)
                            {
                                IStText text = StTextRepository.GetObject(hvo);
                                ExportStText(writer, text, fieldName);
                                fHandled = true;
                            }
                            else
                            {
                                rghvoDest.Add(hvo);
                            }
                        }
                        else
                        {
                            fHandled = true;
                        }
                    }
                    else
                    {
                    }
                }
                break;
                }
                if (!fHandled)
                {
                }
            }
        }
Exemplo n.º 6
0
        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;
                    }
                }
            }
        }
Exemplo n.º 7
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Refresh the lv (list view) control based on the tv (tree view) control.
        /// </summary>
        /// ------------------------------------------------------------------------------------
        public void AfterSelectMethod()
        {
            string holdType = "", holdSig = "";
            int    clid = (int)m_tvModel.SelectedNode.Tag;

            // Get flids.
            int[] uFlids             = m_mdc.GetFields(clid, true, (int)CellarPropertyTypeFilter.All);
            List <ListViewItem> list = new List <ListViewItem>();

            for (int i = uFlids.Length - 1; i >= 0; --i)
            {
                int flid = uFlids[i];
                if (flid == 0)
                {
                    continue;                     // Keep looking for suitable flids lower in the array.
                }
                else
                {
                    if (FDOBrowserForm.m_virtualFlag == false && (flid >= 20000000 && flid < 30000000))
                    {
                        continue;
                    }
                    else
                    {
                        string       className = m_mdc.GetOwnClsName(flid);
                        ListViewItem lvi       = new ListViewItem(className);
                        list.Add(lvi);
                        // flid
                        lvi.SubItems.Add(flid.ToString());
                        // field name
                        string fieldname = m_mdc.GetFieldName(flid);
                        lvi.SubItems.Add(fieldname);
                        int    flidType  = m_mdc.GetFieldType(flid);
                        string type      = "Not recognized";
                        string signature = "Not recognized";
                        int    dstClid;
                        switch (flidType)
                        {
                        // Basic data types.
                        case (int)CellarPropertyType.Boolean:
                            type      = "Basic";
                            signature = "Boolean";
                            break;

                        case (int)CellarPropertyType.Integer:
                            type      = "Basic";
                            signature = "Integer";
                            break;

                        case (int)CellarPropertyType.Numeric:
                            type      = "Basic";
                            signature = "Numeric";
                            break;

                        case (int)CellarPropertyType.Float:
                            type      = "Basic";
                            signature = "Float";
                            break;

                        case (int)CellarPropertyType.Time:
                            type      = "Basic";
                            signature = "Time";
                            break;

                        case (int)CellarPropertyType.Guid:
                            type      = "Basic";
                            signature = "Guid";
                            break;

                        case (int)CellarPropertyType.Image:
                            type      = "Basic";
                            signature = "Image";
                            break;

                        case (int)CellarPropertyType.GenDate:
                            type      = "Basic";
                            signature = "GenDate";
                            break;

                        case (int)CellarPropertyType.Binary:
                            type      = "Basic";
                            signature = "Binary";
                            break;

                        case (int)CellarPropertyType.String:
                            type      = "Basic";
                            signature = "String";
                            break;

                        case (int)CellarPropertyType.MultiString:
                            type      = "Basic";
                            signature = "MultiString";
                            break;

                        case (int)CellarPropertyType.Unicode:
                            type      = "Basic";
                            signature = "Unicode";
                            break;

                        case (int)CellarPropertyType.MultiUnicode:
                            type      = "Basic";
                            signature = "MultiUnicode";
                            break;

                        // CmObjects.
                        case (int)CellarPropertyType.OwningAtomic:
                            type      = "OA";
                            dstClid   = m_mdc.GetDstClsId(flid);
                            signature = m_mdc.GetClassName(dstClid);
                            break;

                        case (int)CellarPropertyType.ReferenceAtomic:
                            type      = "RA";
                            dstClid   = m_mdc.GetDstClsId(flid);
                            signature = m_mdc.GetClassName(dstClid);
                            break;

                        case (int)CellarPropertyType.OwningCollection:
                            type      = "OC";
                            dstClid   = m_mdc.GetDstClsId(flid);
                            signature = m_mdc.GetClassName(dstClid);
                            break;

                        case (int)CellarPropertyType.ReferenceCollection:
                            type      = "RC";
                            dstClid   = m_mdc.GetDstClsId(flid);
                            signature = m_mdc.GetClassName(dstClid);
                            break;

                        case (int)CellarPropertyType.OwningSequence:
                            type      = "OS";
                            dstClid   = m_mdc.GetDstClsId(flid);
                            signature = m_mdc.GetClassName(dstClid);
                            break;

                        case (int)CellarPropertyType.ReferenceSequence:
                            type      = "RS";
                            dstClid   = m_mdc.GetDstClsId(flid);
                            signature = m_mdc.GetClassName(dstClid);
                            break;
                        }

                        if (flid >= 20000000 && flid < 30000000)
                        {
                            type += " (Virt)";
                        }
                        else if (flid > 10000000)
                        {
                            type += " (BackRef)";
                        }

                        lvi.SubItems.Add(type);
                        lvi.SubItems.Add(signature);
                    }
                }
            }

            // Add custom fields

            if (FDOBrowserForm.CFields != null && FDOBrowserForm.CFields.Count > 0)
            {
                foreach (CustomFields cf in FDOBrowserForm.CFields)
                {
                    if (clid == cf.ClassID)
                    {
                        string       clasName = m_mdc.GetClassName(cf.ClassID);
                        ListViewItem lv       = new ListViewItem(clasName);
                        list.Add(lv);
                        // classname
                        //lv.SubItems.Add(clasName);
                        // flid
                        lv.SubItems.Add(cf.FieldID.ToString());
                        // field name
                        lv.SubItems.Add(cf.Name);
                        // Type
                        switch (cf.Type)
                        {
                        case "ICmPossibility":
                            holdType = "Custom - RA";
                            break;

                        case "FdoReferenceCollection<ICmPossibility>":
                            holdType = "Custom - RC";
                            break;

                        case "IStText":
                            holdType = "Custom - OA";
                            break;

                        default:
                            holdType = "Custom";
                            break;
                        }
                        lv.SubItems.Add(holdType);
                        // Signature
                        switch (cf.Type)
                        {
                        case "ITsString":
                            holdSig = "String";
                            break;

                        case "System.Int32":
                            holdSig = "Integer";
                            break;

                        case "SIL.FieldWorks.Common.FwUtils.GenDate":
                            holdSig = "GenDate";
                            break;

                        case "ICmPossibility":
                            holdSig = "CmPossibility";
                            break;

                        case "FdoReferenceCollection<ICmPossibility>":
                            holdSig = "CmPossibility";
                            break;

                        case "IStText":
                            holdSig = "StText";
                            break;

                        default:
                            MessageBox.Show(String.Format("Type not recognized for signature for custom model fields.  Type: {0}", cf.Type));
                            break;
                        }
                        lv.SubItems.Add(holdSig);
                    }
                }
            }
            LoadListView(list);
        }
        /// <summary>
        /// Set custom field data for one field (specified by owner HVO and field ID).
        /// </summary>
        /// <returns><c>true</c>, if custom field data was set, <c>false</c> otherwise (e.g., if value hadn't changed,
        /// or value was null, or field type was one not implemented in LCM, such as CellarPropertyType.Float).</returns>
        /// <param name="hvo">HVO of object whose field we're setting.</param>
        /// <param name="flid">Field ID of custom field to set.</param>
        /// <param name="value">Field's new value (as returned by GetCustomFieldData).</param>
        /// <param name="guidOrGuids">GUID or guids associated with new value (as returned by GetCustomFieldData).
        /// May be null or BsonNull.Value if no GUIDs associated with this value.</param>
        public bool SetCustomFieldData(int hvo, int flid, BsonValue value, BsonValue guidOrGuids)
        {
            if ((value == null) || (value == BsonNull.Value) ||
                ((value.BsonType == BsonType.Array) && (value.AsBsonArray.Count == 0)))
            {
                return(false);
            }
            List <Guid> fieldGuids = new List <Guid>();

            if (guidOrGuids == null || guidOrGuids == BsonNull.Value)
            {
                // Leave fieldGuids as an empty list
            }
            else
            {
                if (guidOrGuids is BsonArray)
                {
                    fieldGuids.AddRange(guidOrGuids.AsBsonArray.Select(bsonValue => ParseGuidOrDefault(bsonValue.AsString)));
                }
                else
                {
                    fieldGuids.Add(ParseGuidOrDefault(guidOrGuids.AsString));
                }
            }
            ISilDataAccessManaged data      = (ISilDataAccessManaged)cache.DomainDataByFlid;
            CellarPropertyType    fieldType = (CellarPropertyType)lcmMetaData.GetFieldType(flid);
            string fieldName = lcmMetaData.GetFieldNameOrNull(flid);

//			logger.Debug("Custom field named {0} has type {1}", fieldName, fieldType.ToString());
            if (fieldName == null)
            {
                return(false);
            }

            // Valid field types in LCM are GenDate, Integer, String, OwningAtomic, ReferenceAtomic, and ReferenceCollection, so that's all we implement.
            switch (fieldType)
            {
            case CellarPropertyType.GenDate:
            {
                var    valueAsMultiText = BsonSerializer.Deserialize <LfMultiText>(value.AsBsonDocument);
                string valueAsString    = valueAsMultiText.BestString(new string[] { MagicStrings.LanguageCodeForGenDateFields });
                if (string.IsNullOrEmpty(valueAsString))
                {
                    return(false);
                }
                GenDate valueAsGenDate;
                if (GenDate.TryParse(valueAsString, out valueAsGenDate))
                {
                    GenDate oldValue = data.get_GenDateProp(hvo, flid);
                    if (oldValue == valueAsGenDate)
                    {
                        return(false);
                    }
                    else
                    {
                        data.SetGenDate(hvo, flid, valueAsGenDate);
                        return(true);
                    }
                }
                return(false);
            }

            case CellarPropertyType.Integer:
            {
                var    valueAsMultiText = BsonSerializer.Deserialize <LfMultiText>(value.AsBsonDocument);
                string valueAsString    = valueAsMultiText.BestString(new string[] { MagicStrings.LanguageCodeForIntFields });
                if (string.IsNullOrEmpty(valueAsString))
                {
                    return(false);
                }
                int valueAsInt;
                if (int.TryParse(valueAsString, out valueAsInt))
                {
                    int oldValue = data.get_IntProp(hvo, flid);
                    if (oldValue == valueAsInt)
                    {
                        return(false);
                    }
                    else
                    {
                        data.SetInt(hvo, flid, valueAsInt);
                        return(true);
                    }
                }
                return(false);
            }

            case CellarPropertyType.OwningAtomic:
            {
                // Custom field is a MultiparagraphText, which is an IStText object in LCM
                IStTextRepository textRepo = servLoc.GetInstance <IStTextRepository>();
                Guid    fieldGuid          = fieldGuids.FirstOrDefault();
                IStText text;
                if (!textRepo.TryGetObject(fieldGuid, out text))
                {
                    int currentFieldContentsHvo = data.get_ObjectProp(hvo, flid);
                    if (currentFieldContentsHvo != LcmCache.kNullHvo)
                    {
                        text = (IStText)cache.GetAtomicPropObject(currentFieldContentsHvo);
                    }
                    else
                    {
                        // NOTE: I don't like the "magic" -2 number below, but LCM doesn't seem to have an enum for this. 2015-11 RM
                        int newStTextHvo = data.MakeNewObject(cache.GetDestinationClass(flid), hvo, flid, -2);
                        text = (IStText)cache.GetAtomicPropObject(newStTextHvo);
                    }
                }
                // Shortcut: if text contents haven't changed, we don't want to change anything at all
                BsonValue currentLcmTextContents = ConvertUtilities.GetCustomStTextValues(text, flid,
                                                                                          servLoc.WritingSystemManager, lcmMetaData, cache.DefaultUserWs);
                if ((currentLcmTextContents == BsonNull.Value || currentLcmTextContents == null) &&
                    (value == BsonNull.Value || value == null))
                {
                    return(false);
                }
                if (currentLcmTextContents != null && currentLcmTextContents.Equals(value))
                {
                    // No changes needed.
                    return(false);
                }
                // BsonDocument passed in contains "paragraphs". ParseCustomStTextValuesFromBson wants only a "value" element
                // inside the doc, so we'll need to construct a new doc for the StTextValues.
                BsonDocument     doc       = value.AsBsonDocument;
                LfMultiParagraph multiPara = BsonSerializer.Deserialize <LfMultiParagraph>(doc);
                // Now we have another way to check for "old value and new value were the same": if the LCM multiparagraph was empty,
                // GetCustomStTextValues will have returned null -- so if this multiPara has no paragraphs, that's also an unchanged situation
                if ((multiPara.Paragraphs == null || multiPara.Paragraphs.Count <= 0) &&
                    (currentLcmTextContents == BsonNull.Value || currentLcmTextContents == null))
                {
                    return(false);
                }
                int wsId;
                if (multiPara.InputSystem == null)
                {
                    wsId = lcmMetaData.GetFieldWs(flid);
                }
                else
                {
                    wsId = servLoc.WritingSystemFactory.GetWsFromStr(multiPara.InputSystem);
                }
                ConvertUtilities.SetCustomStTextValues(text, multiPara.Paragraphs, wsId);

                return(true);
            }

            case CellarPropertyType.ReferenceAtomic:
                if (fieldGuids.FirstOrDefault() != Guid.Empty)
                {
                    int referencedHvo = data.get_ObjFromGuid(fieldGuids.FirstOrDefault());
                    int oldHvo        = data.get_ObjectProp(hvo, flid);
                    if (referencedHvo == oldHvo)
                    {
                        return(false);
                    }
                    else
                    {
                        data.SetObjProp(hvo, flid, referencedHvo);
                        // TODO: What if the value of the referenced object has changed in LanguageForge? (E.g., change that possibility's text from "foo" to "bar")
                        // Need to implement that scenario.
                        return(true);
                    }
                }
                else
                {
                    // It's a reference to an ICmPossibility instance: create a new entry in appropriate PossibilityList
                    LfStringField valueAsLfStringField = BsonSerializer.Deserialize <LfStringField>(value.AsBsonDocument);
                    string        nameHierarchy        = valueAsLfStringField.Value;
                    if (nameHierarchy == null)
                    {
                        return(false);
                    }
                    int fieldWs = lcmMetaData.GetFieldWs(flid);
                    // Oddly, this can return 0 for some custom fields. TODO: Find out why: that seems like it would be an error.
                    if (fieldWs == 0)
                    {
                        fieldWs = cache.DefaultUserWs;                         // TODO: Investigate, because this should probably be wsEn instead so that we can create correct keys.
                    }
                    if (fieldWs < 0)
                    {
                        // FindOrCreatePossibility has a bug where it doesn't handle "magic" writing systems (e.g., -1 for default analysis, etc) and
                        // throws an exception instead. So we need to get a real ws here.
                        fieldWs = WritingSystemServices.ActualWs(cache, fieldWs, hvo, flid);
                    }
                    ICmPossibilityList parentList = GetParentListForField(flid);
                    ICmPossibility     newPoss    = parentList.FindOrCreatePossibility(nameHierarchy, fieldWs);

                    int oldHvo = data.get_ObjectProp(hvo, flid);
                    if (newPoss.Hvo == oldHvo)
                    {
                        return(false);
                    }
                    else
                    {
                        data.SetObjProp(hvo, flid, newPoss.Hvo);
                        return(true);
                    }
                }

            case CellarPropertyType.ReferenceCollection:
            case CellarPropertyType.ReferenceSequence:
            {
                if (value == null || value == BsonNull.Value)
                {
                    // Can't write null to a collection or sequence in LCM; it's forbidden. So data.SetObjProp(hvo, flid, LcmCache.kNullHvo) will not work.
                    // Instead, we delete all items from the existing collection or sequence, and thus store an empty coll/seq in LCM.
                    int oldSize = data.get_VecSize(hvo, flid);
                    if (oldSize == 0)
                    {
                        // It was already empty, so leave it unchanged so we don't cause unnecessary changes in the .fwdata XML (and unnecessary Mercurial commits).
                        return(false);
                    }
                    else
                    {
                        data.Replace(hvo, flid, 0, oldSize, null, 0);                                 // This is how you set an empty array
                        return(true);
                    }
                }
                int fieldWs = lcmMetaData.GetFieldWs(flid);
                // TODO: Investigate why this is sometimes coming back as 0 instead of as a real writing system ID
                if (fieldWs == 0)
                {
                    fieldWs = cache.DefaultUserWs;
                }
                ICmPossibilityList parentList = GetParentListForField(flid);

                LfStringArrayField valueAsStringArray = BsonSerializer.Deserialize <LfStringArrayField>(value.AsBsonDocument);

                // Step 1: Get ICmPossibility instances from the string keys that LF gave us

                // First go through all the GUIDs we have and match them up to the keys. If they match up,
                // then remove the keys from the list. Any remaining keys get looked up with FindOrCreatePossibility(), so now we
                // have a complete set of GUIDs (or ICmPossibility objects, which works out to the same thing).

                // TODO: This is all kind of ugly, and WAY too long for one screen. I could put it in its own function,
                // but there's really no real gain from that, as it simply moves the logic even further away from where
                // it needs to be. There's not really a *good* way to achieve simplicity with this code design, unfortunately.
                // The only thing that would be close to simple would be to call some functions from the LcmToMongo option list
                // converters, and that's pulling in code from the "wrong" direction, which has its own ugliness. Ugh.

                HashSet <string> keysFromLF = new HashSet <string>(valueAsStringArray.Values);
                var fieldObjs = new List <ICmPossibility>();
                foreach (Guid guid in fieldGuids)
                {
                    ICmPossibility poss;
                    string         key = "";
                    if (guid != default(Guid))
                    {
                        poss = servLoc.GetInstance <ICmPossibilityRepository>().GetObject(guid);
                        if (poss == null)
                        {
                            // TODO: Decide what to do with possibilities deleted from LCM
                            key = "";
                        }
                        else
                        {
                            if (poss.Abbreviation == null)
                            {
                                key = "";
                            }
                            else
                            {
                                ITsString keyTss = poss.Abbreviation.get_String(wsEn);
                                key = keyTss == null ? "" : keyTss.Text ?? "";
                            }
                            fieldObjs.Add(poss);
                        }
                    }
                    keysFromLF.Remove(key);
                    // Ignoring return value (HashSet.Remove returns false if the key wasn't present), because false could mean one of two things:
                    // 1. The CmPossibility had its English abbreviation changed in LCM, but LF doesn't know this yet.
                    //    If this is the case, the LF key won't match, but the GUID will still match. So we might end up creating
                    //    duplicate entries below with the FindOrCreatePossibility. TODO: Need to verify that LCM->LF possibility lists
                    //    get updated correctly if renames happen! (... Or use the OptionList converters, that's what they were for.)
                    // 2. The CmPossibility was just created in LF and isn't in LCM yet. In which case we should have been using the
                    //    OptionList converters, which would hopefully have handled creating the ICmPossibility instane in LCM.
                    // Either way, we can't really use that fact later, since we can't be certain if the possibility was renamed or created.
                }
                // Any remaining keysFromLF strings did not have corresponding GUIDs in Mongo.
                // This is most likely because they were added by LF, which doesn't write to the customFieldGuids field.
                // So we assume they exist in FW, and just look them up.
                foreach (string key in keysFromLF)
                {
                    ICmPossibility poss = parentList.FindOrCreatePossibility(key, wsEn);
                    // TODO: If this is a new possibility, then we need to populate it with ALL the corresponding data from LF,
                    // which we don't necessarily have at this point. Need to make that a separate step in the Send/Receive: converting option lists first.
                    fieldObjs.Add(poss);
                }
                // logger.Debug("Custom field {0} for CmObject {1}: BSON list was [{2}] and customFieldGuids was [{3}]. This was translated to keysFromLF = [{4}] and fieldObjs = [{5}]",
                //  fieldName,
                //  hvo,
                //  String.Join(", ", valueAsStringArray.Values),
                //  String.Join(", ", fieldGuids.Select(g => g.ToString())),
                //  String.Join(", ", keysFromLF.AsEnumerable()),
                //  String.Join(", ", fieldObjs.Select(poss => poss.AbbrAndName))
                // );
                // Step 2: Remove any objects from the "old" list that weren't in the "new" list
                // We have to look them up by HVO because that's the only public API available in LCM
                // Following logic inspired by XmlImportData.CopyCustomFieldData in FieldWorks source
                int[] oldHvosArray = data.VecProp(hvo, flid);
                int[] newHvosArray = fieldObjs.Select(poss => poss.Hvo).ToArray();
                // Shortcut check
                if (oldHvosArray.SequenceEqual(newHvosArray))
                {
                    // Nothing to do, so return now so that we don't cause unnecessary changes and commits in Mercurial
                    return(false);
                }
                HashSet <int> newHvos      = new HashSet <int>(newHvosArray);
                HashSet <int> combinedHvos = new HashSet <int>();
                // Loop backwards so deleting items won't mess up indices of subsequent deletions
                for (int idx = oldHvosArray.Length - 1; idx >= 0; idx--)
                {
                    int oldHvo = oldHvosArray[idx];
                    if (newHvos.Contains(oldHvo))
                    {
                        combinedHvos.Add(oldHvo);
                    }
                    else
                    {
                        data.Replace(hvo, flid, idx, idx + 1, null, 0);                                 // Important to pass *both* null *and* 0 here to remove items
                    }
                }

                // Step 3: Add any objects from the "new" list that weren't in the "old" list
                foreach (int newHvo in newHvosArray)
                {
                    if (combinedHvos.Contains(newHvo))
                    {
                        continue;
                    }
                    // This item was added in the new list
                    data.Replace(hvo, flid, combinedHvos.Count, combinedHvos.Count, new int[] { newHvo }, 1);
                    combinedHvos.Add(newHvo);
                }
                return(true);
            }

            case CellarPropertyType.String:
            {
                var    valueAsMultiText          = BsonSerializer.Deserialize <LfMultiText>(value.AsBsonDocument);
                int    wsIdForField              = lcmMetaData.GetFieldWs(flid);
                string wsStrForField             = servLoc.WritingSystemFactory.GetStrFromWs(wsIdForField);
                KeyValuePair <string, string> kv = valueAsMultiText.BestStringAndWs(new string[] { wsStrForField });
                string foundWs   = kv.Key ?? string.Empty;
                string foundData = kv.Value ?? string.Empty;
                int    foundWsId = servLoc.WritingSystemFactory.GetWsFromStr(foundWs);
                if (foundWsId == 0)
                {
                    return(false);                            // Skip any unidentified writing systems
                }
                ITsString oldValue = data.get_StringProp(hvo, flid);
                ITsString newValue = ConvertMongoToLcmTsStrings.SpanStrToTsString(foundData, foundWsId, servLoc.WritingSystemFactory);
                if (oldValue != null && TsStringUtils.GetDiffsInTsStrings(oldValue, newValue) == null)                         // GetDiffsInTsStrings() returns null when there are no changes
                {
                    return(false);
                }
                else
                {
                    data.SetString(hvo, flid, newValue);
                    return(true);
                }
            }

            default:
                return(false);
                // TODO: Maybe issue a proper warning (or error) log message for "field type not recognized"?
            }
        }
Exemplo n.º 9
0
        protected IDictionary <string, Tuple <string, string> > GetLcmDifferences(
            LcmCache cache,
            IDictionary <int, object> fieldValuesBeforeTest,
            IDictionary <int, object> fieldValuesAfterTest
            )
        {
            IFwMetaDataCacheManaged mdc         = cache.ServiceLocator.MetaDataCache;
            var fieldNamesThatShouldBeDifferent = new string[] {
            };
            var fieldNamesToSkip  = new string[] {
                // These are ComObject or SIL.FieldWorks.LCM.DomainImpl.VirtualStringAccessor instances, which we can't compare
                //				"FullReferenceName",
                //				"HeadWord",
                //				"HeadWordRef",
                //				"HeadWordReversal",
                //				"LexSenseOutline",
                //				"MLHeadWord",
                //				"MLOwnerOutlineName",
                //				"ReversalEntriesBulkText",
                //				"ReversalName",
            };
            var differencesByName = new Dictionary <string, Tuple <string, string> >();          // Tuple of (before, after)

            foreach (int flid in fieldValuesBeforeTest.Keys)
            {
                if (mdc.IsCustom(flid))
                {
                    continue;
                }
                string fieldName = mdc.GetFieldNameOrNull(flid);
                if (String.IsNullOrEmpty(fieldName))
                {
                    continue;
                }
                object valueBeforeTest = fieldValuesBeforeTest[flid];
                object valueAfterTest  = fieldValuesAfterTest[flid];

                // Some fields, like DateModified, *should* be different
                if (fieldNamesThatShouldBeDifferent.Contains(fieldName) ||
                    fieldNamesToSkip.Contains(fieldName))
                {
                    continue;
                }

                if ((valueAfterTest == null && valueBeforeTest == null))
                {
                    continue;
                }
                if (mdc.GetFieldType(flid) == (int)CellarPropertyType.String)
                {
                    // Might not need this, see below
                }

                // Arrays need to be compared specially
                Type valueType = valueBeforeTest.GetType();
                if (valueType == typeof(int[]))
                {
                    int[] before = valueBeforeTest as int[];
                    int[] after  = valueAfterTest as int[];
                    if (before.SequenceEqual(after))
                    {
                        continue;
                    }
                }
                // So do TsString objects
                var tsStringBeforeTest = valueBeforeTest as ITsString;
                var tsStringAfterTest  = valueAfterTest as ITsString;
                if (tsStringBeforeTest != null && tsStringAfterTest != null)
                {
                    TsStringDiffInfo diffInfo = TsStringUtils.GetDiffsInTsStrings(tsStringBeforeTest, tsStringAfterTest);
                    if (diffInfo != null)
                    {
                        differencesByName[fieldName] = new Tuple <string, string>(
                            ConvertLcmToMongoTsStrings.TextFromTsString(tsStringBeforeTest, _cache.WritingSystemFactory),
                            ConvertLcmToMongoTsStrings.TextFromTsString(tsStringAfterTest, _cache.WritingSystemFactory)
                            );
                    }
                    continue;
                }
                // So do multistrings
                var multiStrBeforeTest = valueBeforeTest as IMultiAccessorBase;
                var multiStrAfterTest  = valueAfterTest as IMultiAccessorBase;
                if (multiStrBeforeTest != null && multiStrAfterTest != null)
                {
                    int[] wsIds;
                    try
                    {
                        wsIds = multiStrBeforeTest.AvailableWritingSystemIds;
                    }
                    catch (NotImplementedException)
                    {
                        // This is a VirtualStringAccessor, which we can't easily compare. Punt.
                        continue;
                    }
                    foreach (int wsId in wsIds)
                    {
                        string beforeStr = ConvertLcmToMongoTsStrings.TextFromTsString(multiStrBeforeTest.get_String(wsId), _cache.WritingSystemFactory);
                        string afterStr  = ConvertLcmToMongoTsStrings.TextFromTsString(multiStrAfterTest.get_String(wsId), _cache.WritingSystemFactory);
                        if (beforeStr != afterStr)
                        {
                            string wsStr = cache.WritingSystemFactory.GetStrFromWs(wsId);
                            differencesByName[fieldName + ":" + wsStr] = new Tuple <string, string>(beforeStr, afterStr);
                        }
                    }
                    continue;
                }
                if (valueBeforeTest.Equals(valueAfterTest))
                {
                    continue;
                }
                //				if (Repr(valueBeforeTest) == Repr(valueAfterTest))
                //					continue; // This should catch TsStrings
                // If we get this far, they're different
                var diff = new Tuple <string, string>(Repr(fieldValuesBeforeTest[flid]), Repr(fieldValuesAfterTest[flid]));
                differencesByName[fieldName] = diff;
            }
            return(differencesByName);
        }
Exemplo n.º 10
0
		private static Dictionary<string, List<PropertyInfo>> CacheBasicProperties(IFwMetaDataCacheManaged mdc)
		{
			var cachedBasicProperties = new Dictionary<string, List<PropertyInfo>>();
			foreach (var classId in mdc.GetClassIds())
			{
				var className = mdc.GetClassName(classId);
				List<PropertyInfo> basicProps;
				if (!cachedBasicProperties.TryGetValue(className, out basicProps))
				{
					basicProps = new List<PropertyInfo>();
					cachedBasicProperties.Add(className, basicProps);
				}
				basicProps.AddRange(mdc.GetFields(classId, className != "CmObject", (int)CellarPropertyTypeFilter.AllBasic).Select(propId => new PropertyInfo
					{
						m_propertyName = mdc.GetFieldName(propId),
						m_propertyType = (CellarPropertyType)mdc.GetFieldType(propId),
						m_isCustom = mdc.IsCustom(propId),
						m_isVirtual = mdc.get_IsVirtual(propId)
					}));
				if (basicProps.Count == 0)
					cachedBasicProperties.Remove(className);
			}
			return cachedBasicProperties;
		}