/// <summary>
        /// Creates deep copy
        /// </summary>
        public override AbstractLocalizationCriterion DeepCopy()
        {
            LocalizationCustomCriterion crit = new LocalizationCustomCriterion();

            InternalDeepCopy(crit);
            crit.Predicate = Predicate;
            crit.Regex     = Regex == null ? null : (string)Regex.Clone();
            crit.Target    = Target;
            return(crit);
        }
        /// <summary>
        /// When "Apply" button of the settings dialog was hit; save the GUI state in the settings
        /// </summary>
        protected override void OnApply(PageApplyEventArgs e)
        {
            if (e.ApplyBehavior == ApplyKind.Apply)
            {
                try {
                    SettingsObject.Instance.IgnorePropertyChanges = true;

                    bool revalidate = SettingsObject.Instance.ShowContextColumn != contextBox.Checked;
                    SettingsObject.Instance.ShowContextColumn  = contextBox.Checked;
                    SettingsObject.Instance.UseReflectionInAsp = reflectionBox.Checked;

                    foreach (var pair in SettingsObject.Instance.CommonLocalizabilityCriteria)
                    {
                        LocalizationCommonCriterion crit = pair.Value;
                        DataGridViewRow             row  = commonCriteriaGrid.Rows[commonCriteriaRowMap[crit.Name]];

                        crit.Action = (LocalizationCriterionAction)actionColumn.Items.IndexOf(row.Cells[1].Value);

                        int    w   = 0;
                        object val = row.Cells[2].Value;
                        if (val == null || !int.TryParse(val.ToString(), out w) || w < -100 || w > 100)
                        {
                            throw new Exception("Error on '" + crit.Description + "' - invalid value '" + val + "'. Please enter an integer from -100 to 100.");
                        }

                        crit.Weight = w;
                    }

                    SettingsObject.Instance.CustomLocalizabilityCriteria.Clear();
                    for (int i = 0; i < customCriteriaTable.RowCount - 1; i++)
                    {
                        TableLayoutPanel t            = (TableLayoutPanel)customCriteriaTable.GetControlFromPosition(0, i);
                        ComboBox         targetBox    = (ComboBox)t.GetControlFromPosition(1, 0);
                        ComboBox         predicateBox = (ComboBox)t.GetControlFromPosition(2, 0);
                        TextBox          regexBox     = (TextBox)t.GetControlFromPosition(1, 1);
                        ComboBox         actionBox    = (ComboBox)t.GetControlFromPosition(1, 2);
                        NumericUpDown    valueBox     = (NumericUpDown)t.GetControlFromPosition(2, 2);

                        if (actionBox.SelectedIndex == -1)
                        {
                            throw new Exception("Error on custom rule no. " + (i + 1) + " - must select action.");
                        }
                        LocalizationCriterionAction action = (LocalizationCriterionAction)actionBox.SelectedIndex;

                        LocalizationCustomCriterion crit = new LocalizationCustomCriterion(action, (int)valueBox.Value);
                        if (!string.IsNullOrEmpty(t.Name))
                        {
                            crit.Name = t.Name;
                        }

                        if (predicateBox.SelectedIndex == -1)
                        {
                            throw new Exception("Error on custom rule no. " + (i + 1) + " - must select predicate.");
                        }
                        crit.Predicate = (LocalizationCriterionPredicate)predicateBox.SelectedIndex;

                        if (targetBox.SelectedIndex == -1)
                        {
                            throw new Exception("Error on custom rule no. " + (i + 1) + " - must select target.");
                        }
                        crit.Target = (LocalizationCriterionTarget)targetBox.SelectedIndex;

                        new Regex(regexBox.Text); // throws exception if regex is not valid
                        crit.Regex = regexBox.Text;

                        SettingsObject.Instance.CustomLocalizabilityCriteria.Add(crit);
                    }

                    if (revalidate)
                    {
                        SettingsObject.Instance.NotifyRevalidationRequested();
                    }
                } catch (Exception ex) {
                    VLOutputWindow.VisualLocalizerPane.WriteException(ex);
                    VisualLocalizer.Library.Components.MessageBox.ShowException(ex);
                    e.ApplyBehavior = ApplyKind.CancelNoNavigate;
                } finally {
                    SettingsObject.Instance.IgnorePropertyChanges = false;
                    SettingsObject.Instance.NotifyPropertyChanged(CHANGE_CATEGORY.FILTER);
                }
            }
        }
        /// <summary>
        /// Loads settings from registry storage (on package load)
        /// </summary>
        public override void LoadSettingsFromStorage()
        {
            VisualLocalizerPackage package     = VisualLocalizerPackage.Instance;
            RegistryKey            rootKey     = package.UserRegistryRoot;
            RegistryKey            settingsKey = null;

            try {
                settingsKey = rootKey.OpenSubKey(REG_KEY, false);
                if (settingsKey != null)
                {
                    RegistryKey filtersKey = settingsKey.OpenSubKey(FILTER_KEY);

                    if (filtersKey != null)
                    {
                        SettingsObject.Instance.IgnorePropertyChanges = true;

                        SettingsObject.Instance.ShowContextColumn              = ReadBoolFromRegKey(filtersKey, "ShowFilterContext");
                        SettingsObject.Instance.UseReflectionInAsp             = ReadBoolFromRegKey(filtersKey, "UseReflectionInAsp");
                        SettingsObject.Instance.NamespacePolicyIndex           = ReadIntFromRegKey(filtersKey, "NamespacePolicyIndex");
                        SettingsObject.Instance.MarkNotLocalizableStringsIndex = ReadIntFromRegKey(filtersKey, "MarkNotLocalizableStringsIndex");
                        SettingsObject.Instance.BatchMoveSplitterDistance      = ReadIntFromRegKey(filtersKey, "BatchMoveSplitterDistance", 110);
                        SettingsObject.Instance.SelectedKeyIndex         = ReadIntFromRegKey(filtersKey, "SelectedKeyIndex", 0);
                        SettingsObject.Instance.SelectedUseFullName      = ReadBoolFromRegKey(filtersKey, "SelectedUseFullName");
                        SettingsObject.Instance.SelectedResourceFilePath = (string)filtersKey.GetValue("SelectedResourceFilePath", null);

                        foreach (var pair in SettingsObject.Instance.CommonLocalizabilityCriteria)
                        {
                            LocalizationCommonCriterion crit = pair.Value;
                            object val = filtersKey.GetValue(crit.Name);
                            if (val != null)
                            {
                                crit.FromRegData(val.ToString());
                            }
                        }

                        SettingsObject.Instance.CustomLocalizabilityCriteria.Clear();
                        int customCriteriaCount = ReadIntFromRegKey(filtersKey, "CustomCriteriaCount", 0);
                        for (int i = 0; i < customCriteriaCount; i++)
                        {
                            object val = filtersKey.GetValue("CustomCriteria" + i);
                            if (val != null)
                            {
                                LocalizationCustomCriterion crit = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, 0);
                                crit.FromRegData(val.ToString());
                                SettingsObject.Instance.CustomLocalizabilityCriteria.Add(crit);
                            }
                        }
                    }
                    else
                    {
                        ResetSettings();
                    }
                }
                else
                {
                    ResetSettings();
                }
            } finally {
                if (settingsKey != null)
                {
                    settingsKey.Close();
                }

                SettingsObject.Instance.IgnorePropertyChanges = false;
                SettingsObject.Instance.NotifySettingsLoaded();
            }
        }
        /// <summary>
        /// Never called by VS (bug?)
        /// </summary>
        public override void ResetSettings()
        {
            SettingsObject.Instance.IgnorePropertyChanges = true;

            SettingsObject.Instance.MarkNotLocalizableStringsIndex = 0;
            SettingsObject.Instance.NamespacePolicyIndex           = 0;
            SettingsObject.Instance.BatchMoveSplitterDistance      = 130;
            SettingsObject.Instance.ShowContextColumn        = false;
            SettingsObject.Instance.UseReflectionInAsp       = true;
            SettingsObject.Instance.SelectedKeyIndex         = 0;
            SettingsObject.Instance.SelectedResourceFilePath = null;
            SettingsObject.Instance.SelectedUseFullName      = false;

            SettingsObject.Instance.ResetCriteria();

            // create list of default criteria
            var valueIsFormat = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, 20);

            valueIsFormat.Predicate = LocalizationCriterionPredicate.MATCHES;
            valueIsFormat.Target    = LocalizationCriterionTarget.VALUE;
            valueIsFormat.Regex     = @"{\d+(:.+)?}(?# formatting placeholders)";
            valueIsFormat.Name      = Path.GetRandomFileName();
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(valueIsFormat);

            var valueIsMultiline = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_ENABLE, 0);

            valueIsMultiline.Predicate = LocalizationCriterionPredicate.MATCHES;
            valueIsMultiline.Target    = LocalizationCriterionTarget.VALUE;
            valueIsMultiline.Regex     = "\\w+.*\r\n\\w+.*(?# end of line characters)";
            valueIsMultiline.Name      = Path.GetRandomFileName();
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(valueIsMultiline);

            var valueNoLetters = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_DISABLE, 0);

            valueNoLetters.Predicate = LocalizationCriterionPredicate.NO_LETTERS;
            valueNoLetters.Target    = LocalizationCriterionTarget.VALUE;
            valueNoLetters.Regex     = string.Empty;
            valueNoLetters.Name      = Path.GetRandomFileName();
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(valueNoLetters);

            var valueCaps = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_DISABLE, 0);

            valueCaps.Predicate = LocalizationCriterionPredicate.ONLY_CAPS;
            valueCaps.Target    = LocalizationCriterionTarget.VALUE;
            valueCaps.Regex     = string.Empty;
            valueCaps.Name      = Path.GetRandomFileName();
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(valueCaps);

            var valueWhitespace = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, 30);

            valueWhitespace.Predicate = LocalizationCriterionPredicate.MATCHES;
            valueWhitespace.Target    = LocalizationCriterionTarget.VALUE;
            valueWhitespace.Regex     = @"\w+\s+\w+(?# contains whitespace)";
            valueWhitespace.Name      = Path.GetRandomFileName();
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(valueWhitespace);

            var smallLength = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -60);

            smallLength.Predicate = LocalizationCriterionPredicate.DOESNT_MATCH;
            smallLength.Target    = LocalizationCriterionTarget.VALUE;
            smallLength.Name      = Path.GetRandomFileName();
            smallLength.Regex     = "..(?# too short)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(smallLength);

            var ipv4Address = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_DISABLE, 0);

            ipv4Address.Predicate = LocalizationCriterionPredicate.MATCHES;
            ipv4Address.Target    = LocalizationCriterionTarget.VALUE;
            ipv4Address.Name      = Path.GetRandomFileName();
            ipv4Address.Regex     = @"^\d+\.\d+\.\d+.\d+$(?# IPv4 address)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(ipv4Address);

            var ipv6Address = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_DISABLE, 0);

            ipv6Address.Predicate = LocalizationCriterionPredicate.MATCHES;
            ipv6Address.Target    = LocalizationCriterionTarget.VALUE;
            ipv6Address.Name      = Path.GetRandomFileName();
            ipv6Address.Regex     = @"^[0-9a-fA-F]+(:?([0-9a-fA-F]+)?)*:[0-9a-fA-F]+$(?# IPv6 address)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(ipv6Address);

            var link = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_DISABLE, 0);

            link.Predicate = LocalizationCriterionPredicate.MATCHES;
            link.Target    = LocalizationCriterionTarget.VALUE;
            link.Name      = Path.GetRandomFileName();
            link.Regex     = @"^(http|https|ftp)://.*$(?# URL)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(link);

            var camelCase = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -80);

            camelCase.Predicate = LocalizationCriterionPredicate.MATCHES;
            camelCase.Target    = LocalizationCriterionTarget.VALUE;
            camelCase.Name      = Path.GetRandomFileName();
            camelCase.Regex     = @"^[0-9A-Z]?\w+([0-9A-Z](\w+)?)+$(?# camel case)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(camelCase);

            var reference = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -60);

            reference.Predicate = LocalizationCriterionPredicate.MATCHES;
            reference.Target    = LocalizationCriterionTarget.VALUE;
            reference.Name      = Path.GetRandomFileName();
            reference.Regex     = @"^\w+(\.\w+)*\.\w+$(?# reference)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(reference);

            var filepath = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -40);

            filepath.Predicate = LocalizationCriterionPredicate.MATCHES;
            filepath.Target    = LocalizationCriterionTarget.VALUE;
            filepath.Name      = Path.GetRandomFileName();
            filepath.Regex     = @"^.+([/\\].+)*[/\\].+$(?# file path)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(filepath);

            var menuitem = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, 30);

            menuitem.Predicate = LocalizationCriterionPredicate.MATCHES;
            menuitem.Target    = LocalizationCriterionTarget.VALUE;
            menuitem.Name      = Path.GetRandomFileName();
            menuitem.Regex     = @"^.*&\w.*$(?# menu item text)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(menuitem);

            var relativePath = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -60);

            relativePath.Predicate = LocalizationCriterionPredicate.MATCHES;
            relativePath.Target    = LocalizationCriterionTarget.VALUE;
            relativePath.Name      = Path.GetRandomFileName();
            relativePath.Regex     = @"^~[/\\].*$(?# relative path)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(relativePath);

            var sqlQuery = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -80);

            sqlQuery.Predicate = LocalizationCriterionPredicate.MATCHES;
            sqlQuery.Target    = LocalizationCriterionTarget.VALUE;
            sqlQuery.Name      = Path.GetRandomFileName();
            sqlQuery.Regex     = @"^\s*(SELECT|UPDATE|DELETE|CREATE|DROP|ALTER|REMOVE|INSERT|SHOW|TRUNCATE).*$(?# SQL query)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(sqlQuery);

            var email = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -60);

            email.Predicate = LocalizationCriterionPredicate.MATCHES;
            email.Target    = LocalizationCriterionTarget.VALUE;
            email.Name      = Path.GetRandomFileName();
            email.Regex     = @"^\w+@\w+\.\w+.*$(?# email)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(email);

            var style = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, -80);

            style.Predicate = LocalizationCriterionPredicate.MATCHES;
            style.Target    = LocalizationCriterionTarget.ELEMENT_NAME;
            style.Name      = Path.GetRandomFileName();
            style.Regex     = @"^style$(?# CSS)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(style);

            var names = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_DISABLE, 0);

            names.Predicate = LocalizationCriterionPredicate.MATCHES;
            names.Target    = LocalizationCriterionTarget.LINE;
            names.Name      = Path.GetRandomFileName();
            names.Regex     = @".+\.Name =(?# something.Name = <string>)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(names);

            var diacritics = new LocalizationCustomCriterion(LocalizationCriterionAction.FORCE_ENABLE, 0);

            diacritics.Predicate = LocalizationCriterionPredicate.MATCHES;
            diacritics.Target    = LocalizationCriterionTarget.VALUE;
            diacritics.Name      = Path.GetRandomFileName();
            diacritics.Regex     = @"[ěščřžýáíéúůďťňóĚŠČŘŽÝÁÍÉÚŮĎŤÓöüäëÖÜÄËľĽĹĺôÔ](?# common culture-specific characters)";
            SettingsObject.Instance.CustomLocalizabilityCriteria.Add(diacritics);

            SettingsObject.Instance.IgnorePropertyChanges = false;
            SettingsObject.Instance.NotifyPropertyChanged(CHANGE_CATEGORY.FILTER);
            SettingsObject.Instance.NotifySettingsLoaded();
        }
        /// <summary>
        /// Loads settings from XML (on import settings)
        /// </summary>
        public override void LoadSettingsFromXml(IVsSettingsReader reader)
        {
            SettingsObject.Instance.IgnorePropertyChanges = true;

            SettingsObject.Instance.ShowContextColumn              = ReadBoolFromXml(reader, "ShowFilterContext");
            SettingsObject.Instance.UseReflectionInAsp             = ReadBoolFromXml(reader, "UseReflectionInAsp");
            SettingsObject.Instance.MarkNotLocalizableStringsIndex = ReadIntFromXml(reader, "MarkNotLocalizableStringsIndex");
            SettingsObject.Instance.NamespacePolicyIndex           = ReadIntFromXml(reader, "NamespacePolicyIndex");
            SettingsObject.Instance.BatchMoveSplitterDistance      = ReadIntFromXml(reader, "BatchMoveSplitterDistance");
            SettingsObject.Instance.SelectedKeyIndex    = ReadIntFromXml(reader, "SelectedKeyIndex");
            SettingsObject.Instance.SelectedUseFullName = ReadBoolFromXml(reader, "SelectedUseFullName");

            int    hr;
            string tmp;

            hr = reader.ReadSettingString("SelectedResourceFilePath", out tmp);
            SettingsObject.Instance.SelectedResourceFilePath = tmp;
            Marshal.ThrowExceptionForHR(hr);

            foreach (var pair in SettingsObject.Instance.CommonLocalizabilityCriteria)
            {
                LocalizationCommonCriterion crit = pair.Value;

                string val;
                hr = reader.ReadSettingString(crit.Name, out val);
                if (hr != VSConstants.S_OK)
                {
                    throw new Exception("Error retrieving data from XML.");
                }

                if (val != null)
                {
                    crit.FromRegData(val.ToString());
                }
            }

            SettingsObject.Instance.CustomLocalizabilityCriteria.Clear();
            int customCriteriaCount = 0;

            hr = reader.ReadSettingLong("CustomCriteriaCount", out customCriteriaCount);
            if (hr != VSConstants.S_OK)
            {
                throw new Exception("Error retrieving data from XML.");
            }

            for (int i = 0; i < customCriteriaCount; i++)
            {
                string val;
                hr = reader.ReadSettingString("CustomCriteria" + i, out val);

                if (val != null)
                {
                    LocalizationCustomCriterion crit = new LocalizationCustomCriterion(LocalizationCriterionAction.VALUE, 0);
                    crit.FromRegData(val.ToString());
                    SettingsObject.Instance.CustomLocalizabilityCriteria.Add(crit);
                }
            }

            SettingsObject.Instance.IgnorePropertyChanges = false;
            SettingsObject.Instance.NotifySettingsLoaded();
        }