/// <summary>
 /// Deserializes xml markup from file into an xpathref object
 /// </summary>
 /// <param name="fileName">string xml file to load and deserialize</param>
 /// <param name="obj">Output xpathref object</param>
 /// <param name="exception">output Exception value if deserialize failed</param>
 /// <returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns>
 public static bool LoadFromFile(string fileName, out xpathref obj, out System.Exception exception)
 {
     exception = null;
     obj = default(xpathref);
     try
     {
         obj = LoadFromFile(fileName);
         return true;
     }
     catch (System.Exception ex)
     {
         exception = ex;
         return false;
     }
 }
 public static bool LoadFromFile(string fileName, out xpathref obj)
 {
     System.Exception exception = null;
     return LoadFromFile(fileName, out obj, out exception);
 }
 public static bool Deserialize(string xml, out xpathref obj)
 {
     System.Exception exception = null;
     return Deserialize(xml, out obj, out exception);
 }
 /// <summary>
 /// Deserializes workflow markup into an xpathref object
 /// </summary>
 /// <param name="xml">string workflow markup to deserialize</param>
 /// <param name="obj">Output xpathref object</param>
 /// <param name="exception">output Exception value if deserialize failed</param>
 /// <returns>true if this XmlSerializer can deserialize the object; otherwise, false</returns>
 public static bool Deserialize(string xml, out xpathref obj, out System.Exception exception)
 {
     exception = null;
     obj = default(xpathref);
     try
     {
         obj = Deserialize(xml);
         return true;
     }
     catch (System.Exception ex)
     {
         exception = ex;
         return false;
     }
 }
        private void buttonOK_Click(object sender, EventArgs e)
        {
            string titleText = "";
            String newXPath = null;
            string pred;
            TagData td;

            if (listBoxTypeFilter.SelectedItem != null
                && listBoxTypeFilter.SelectedItem.ToString().Equals(Helpers.QuestionListHelper.REPEAT_POS))
            {
                // Special case
                newXPath = null;
                pred = (string)this.listBoxPredicate.SelectedItem;

                if (pred.Equals("first"))
                {
                    newXPath = "position()=1";
                    titleText = "If first entry in repeat";
                }
                else if (pred.Equals("not first"))
                {
                    newXPath = "position()&gt;1";
                    titleText = "If not the first entry in repeat";
                }
                else if (pred.Equals("second"))
                {
                    newXPath = "position()=2";
                    titleText = "If second entry in repeat";
                }
                else if (pred.Equals("second last"))
                {
                    newXPath = "position()=last()-1";
                    titleText = "If second last entry in repeat";

                }
                else if (pred.Equals("last"))
                {
                    newXPath = "position()=last()";
                    titleText = "If last entry in repeat";
                }
                else if (pred.Equals("not last"))
                {
                    newXPath = "position()!=last()";
                    titleText = "If not the last entry in repeat";
                }
                else
                {
                    log.Error("unexpected predicate " + pred);
                }
                // No point making this a condition

                //condition result = conditionsHelper.setup(xpathExisting.dataBinding.storeItemID,
                //    newXPath, xpathExisting.dataBinding.prefixMappings, false);

                xpathsXpath xpathEntry = xppe.setup("", model.answersPart.Id, newXPath, null, false);
                //xpathEntry.questionID = q.id;
                xpathEntry.dataBinding.prefixMappings = "xmlns:oda='http://opendope.org/answers'";
                //xpathEntry.type = dataType;
                xppe.save();

                td = new TagData("");
                td.set("od:RptPosCon", xpathEntry.id);
                cc.Tag = td.asQueryString();

                cc.Title = titleText;
                cc.SetPlaceholderText(null, null, "Type the text that'll appear between repeated items.");
                // that'll only be displayed if the cc is not being wrapped around existing content :-)

                // Don't :
                // ContentControlNewConditionCheck variableRelocator = new ContentControlNewConditionCheck();
                // variableRelocator.checkAnswerAncestry(xpathExisting.id);

                postconditionsMet = true;
                return;

            }

            xpathsXpath xpathExisting = null;
            string val = null;

            // Validation
            question q = (question)listBoxQuestions.SelectedItem;
            if (q == null)
            {
                MessageBox.Show("You must select a question!");
                DialogResult = DialogResult.None; // or use on closing event; see http://stackoverflow.com/questions/2499644/preventing-a-dialog-from-closing-in-the-buttons-click-event-handler
                return;
            }
            else
            {
                // Get the XPath for the selected question
                xpathExisting = xppe.getXPathByQuestionID(q.id);
            }

            //if (listBoxTypeFilter.SelectedItem != null
            //    && listBoxTypeFilter.SelectedItem.ToString().Equals("repeat")) {
            //        // Special case
            //    }
            //else
            //{
                //More validation

                object o = this.comboBoxValues.SelectedItem;
                if (o==null)
                {
                    if (comboBoxValues.Text == null)
                    {
                        MessageBox.Show("You must specify a value!");
                        DialogResult = DialogResult.None;
                        return;
                    }
                    else
                    {
                        o = comboBoxValues.Text;
                    }
                }
                if (o is string)
                {
                    val = (string)o;
                }
                else
                {
                    //responseFixed
                    val = ((responseFixedItem)o).value;
                }
            //}
            ConditionsPartEntry conditionsHelper = new ConditionsPartEntry(model);

            //if (xpathExisting!=null && xpathExisting.type.Equals("boolean")
            //    && (val.ToLower().Equals("true")
            //    || val.ToLower().Equals("false"))) {
            //    // if its boolean true, all we need to do is create a condition pointing to that
            //    // if its boolean false, all we need to do is create a condition not pointing to that

            //    TagData td = new TagData("");

            //    if (val.ToLower().Equals("true") )
            //    {
            //        // if its boolean true, all we need to do is create a condition pointing to that
            //        log.Info("boolean true - just need a condition");

            //        condition c = conditionsHelper.setup(xpathExisting);
            //        td.set("od:condition", c.id);
            //        cc.Tag = td.asQueryString();

            //        titleText = "If '" + val + "' for Q: " + q.text;
            //    }
            //    else if (val.ToLower().Equals("false") )
            //    {
            //        // if its boolean false, all we need to do is create a condition not pointing to that
            //        log.Info("boolean true - just need a NOT condition");
            //        condition c = new condition();

            //        xpathref xpathref = new xpathref();
            //        xpathref.id = xpathExisting.id;

            //        not n = new not();
            //        n.Item = xpathref;

            //        c.Item = n;

            //        conditionsHelper.add(c, "not" + xpathref.id);
            //        td.set("od:condition", c.id);
            //        cc.Tag = td.asQueryString();

            //        titleText = "If '" + val + "' for Q: " + q.text;
            //    }
            //    else
            //    {
            //        MessageBox.Show("Only true/yes or false/no are allowed for this question");
            //        return;
            //    }

            //} else {

                // otherwise, create a new xpath object, and a condition pointing to it
                pred = (string)this.listBoxPredicate.SelectedItem;

                if (pred == null)
                {
                    MessageBox.Show("For " + xpathExisting.type + ", you must select a relation!");
                    DialogResult = DialogResult.None;
                    return;
                }

                log.Info("create a new xpath object, and a condition pointing to it.  Predicate is " + pred);

                newXPath = null;

                if (listBoxTypeFilter.SelectedItem != null
                    && listBoxTypeFilter.SelectedItem.ToString().Equals(Helpers.QuestionListHelper.REPEAT_COUNT))
                {
                    if (pred.Equals("="))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")=" + val;
                        titleText = "If Repeat " + q.text + " has " + val;
                    }
                    else if (pred.Equals(">"))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")>" + val;
                        titleText = "If Repeat " + q.text + " > " + val;
                    }
                    else if (pred.Equals(">="))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")>=" + val;
                        titleText = "If Repeat " + q.text + " >= " + val;
                    }
                    else if (pred.Equals("<"))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")<" + val;
                        titleText = "If Repeat " + q.text + " < " + val;

                    }
                    else if (pred.Equals("<="))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")<=" + val;
                        titleText = "If Repeat " + q.text + " <= " + val;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }

                } else if (xpathExisting.type.Equals("boolean")) {

                    // done this way, since XPath spec says the boolean value of a string is true,
                    // if it is not empty!

                    newXPath = "string(" + xpathExisting.dataBinding.xpath + ")='" + val + "'";
                    titleText = "If '" + val + "' for Q: " + q.text;

                } else if (xpathExisting.type.Equals("string"))
                {
                    if (pred.Equals("equals"))
                    {
                        newXPath = "string(" + xpathExisting.dataBinding.xpath + ")='" + val + "'";
                        titleText = "If '" + val + "' for Q: " + q.text;

                    }
                    else if (pred.Equals("is not"))
                    {
                        newXPath = "string(" + xpathExisting.dataBinding.xpath + ")!='" + val + "'";
                        titleText = "If NOT '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals("starts-with"))
                    {
                        newXPath = "starts-with(string(" + xpathExisting.dataBinding.xpath + "), '" + val + "')";
                        titleText = "If starts-with '" + val + "' for Q: " + q.text;

                    }
                    else if (pred.Equals("contains"))
                    {
                        newXPath = "contains(string(" + xpathExisting.dataBinding.xpath + "), '" + val + "')";
                        titleText = "If contains '" + val + "' for Q: " + q.text;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }
                } else if (xpathExisting.type.Equals("decimal")
                    || xpathExisting.type.Equals("integer")
                    || xpathExisting.type.Equals("positiveInteger")
                    || xpathExisting.type.Equals("nonPositiveInteger")
                    || xpathExisting.type.Equals("negativeInteger")
                    || xpathExisting.type.Equals("nonNegativeInteger")
                    )
                {
                    if (pred.Equals("="))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")=" + val;
                        titleText = "If '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals(">"))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")>" + val;
                        titleText = "If >" + val + " for Q: " + q.text;
                    }
                    else if (pred.Equals(">="))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")>=" + val;
                        titleText = "If >=" + val + " for Q: " + q.text;
                    }
                    else if (pred.Equals("<"))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")<" + val;
                        titleText = "If <" + val + " for Q: " + q.text;

                    }
                    else if (pred.Equals("<="))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")<=" + val;
                        titleText = "If <=" + val + " for Q: " + q.text;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }

                }
                else if (xpathExisting.type.Equals("date"))
                {
                    // Requires XPath 2.0

                    if (pred.Equals("equals"))
                    {
                        newXPath = "xs:date(" + xpathExisting.dataBinding.xpath + ") = xs:date('" + val + "')";
                        titleText = "If '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals("is before"))
                    {
                        newXPath = "xs:date(" + xpathExisting.dataBinding.xpath + ") < xs:date('" + val + "')";
                        titleText = "If before '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals("is after"))
                    {
                        newXPath = "xs:date(" + xpathExisting.dataBinding.xpath + ") > xs:date('" + val + "')";
                        titleText = "If after '" + val + "' for Q: " + q.text;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }

                } else
                {
                    log.Error("Unexpected data type " + xpathExisting.type);
                }

                if (existingCondition == null)
                {
                    // Create new condition

                    condition result = conditionsHelper.setup(xpathExisting.dataBinding.storeItemID,
                        newXPath, xpathExisting.dataBinding.prefixMappings, false);
                    td = new TagData("");
                    td.set("od:condition", result.id);
                    cc.Tag = td.asQueryString();

                    //}

                    cc.SetPlaceholderText(null, null, "Type the text for when this condition is satisfied.");
                    // that'll only be displayed if the cc is not being wrapped around existing content :-)
                }
                else
                {
                    // Update existing condition

                    // Drop any trailing "/" from a Condition XPath
                    if (newXPath.EndsWith("/"))
                    {
                        newXPath = newXPath.Substring(0, newXPath.Length - 1);
                    }
                    log.Debug("Creating condition using XPath:" + newXPath);

                    XPathsPartEntry xppe = new XPathsPartEntry(model);
                    xpathsXpath xpath = xppe.setup("cond", xpathExisting.dataBinding.storeItemID,
                        newXPath, xpathExisting.dataBinding.prefixMappings, false);
                    xppe.save();

                    xpathref xpathref = new xpathref();
                    xpathref.id = xpath.id;

                    // NB no attempt is made here to delete the old xpathref
                    // TODO

                    existingCondition.Item = xpathref;

                    // Save the conditions in docx
                    cpe.save();

                }
                cc.Title = titleText;

                if (listBoxTypeFilter.SelectedItem != null
                    && listBoxTypeFilter.SelectedItem.ToString().Equals(Helpers.QuestionListHelper.REPEAT_COUNT))
                {
                    // Skip ContentControlNewConditionCheck
                }
                else
                {
                    // Make sure this question is allowed here
                    // ie the it is top level or in a common repeat ancestor.
                    // We do this last, so this cc has od:condition on it,
                    // in which case we can re-use existing code to do the check
                    // TODO: when we support and/or, will need to do this
                    // for each variable.
                    ContentControlNewConditionCheck variableRelocator = new ContentControlNewConditionCheck();
                    variableRelocator.checkAnswerAncestry(xpathExisting.id);
                }
            postconditionsMet = true;
        }
        /// <summary>
        /// Add the condition to the conditions part.  
        /// </summary>
        /// <param name="model"></param>
        /// <param name="cxpId"></param>
        /// <param name="strXPath"></param>
        /// <param name="prefixMappings"></param>
        public condition setup(xpathsXpath xpath)
        {
            condition result = null;

            //////////////////////////
            // Second, create and add the condition

            // If the Condition is already defined in our Condition part, don't do it again.
            // Also need this for ID generation.

            Dictionary<string, string> conditionsById = new Dictionary<string, string>();
            foreach (condition xx in conditions.condition)
            {
                conditionsById.Add(xx.id, "");
                if (xx.Item is xpathref)
                {
                    xpathref ex = (xpathref)xx.Item;

                    if (ex.id.Equals(xpath.id))
                    {
                        result = xx;
                        log.Info("This Condition is already setup, with ID: " + xx.id);
                        break;
                    }
                }
            }

            if (result == null) // not already defined
            {
                // Add the new condition
                result = new condition();
                //result.id = IdGenerator.generateIdForXPath(conditionsById, null, null, xpath.dataBinding.xpath);
                result.id = IdHelper.GenerateShortID(5);

                xpathref xpathref = new xpathref();
                xpathref.id = xpath.id;

                result.Item = xpathref;

                conditions.condition.Add(result);

                // Save the conditions in docx
                string ser = conditions.Serialize();
                log.Info(ser);
                CustomXmlUtilities.replaceXmlDoc(model.conditionsPart, ser);
            }

            // Set this
            conditionId = result.id;

            log.Debug("Condition written!");

            return result;
        }
        private void buildCondition()
        {
            ConditionsPartEntry cpe = new ConditionsPartEntry(model);
            TagData td;

            if (this.dataGridView.Rows.Count == 2 // auto last row
                &&  !this.listBoxGovernor.SelectedItem.ToString().Equals("none") ) // none handled separately
            {
                // a simple condition

                if (this.dataGridView.Rows[0].Cells["Questions"].Value is condition)
                {
                    // this is just condition re-use!
                    condition cReused = (condition)this.dataGridView.Rows[0].Cells["Questions"].Value;
                    setTag(cc, cReused);
                    cc.Title = cReused.description; // that'll do for now
                    return;
                }

                // Usual case
                Pairing pair = buildXPathRef(this.dataGridView.Rows[0]);
                cc.Title = restrict64chars(pair.titleText);

                if (pair.xpathEntry.dataBinding.xpath.Contains("position()"))
                {
                    // special case.  TODO: make this a normal condition!
                    // since this approach won't work if it is in complex condition
                    td = new TagData("");
                    td.set("od:RptPosCon", pair.xpathEntry.id);
                    cc.Tag = td.asQueryString();

                    cc.SetPlaceholderText(null, null, "Type the text that'll appear between repeated items.");
                    // that'll only be displayed if the cc is not being wrapped around existing content :-)
                    return;
                }

                condition result = cpe.setup(pair.xpathEntry);
                result.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    result.description = pair.titleText;
                }
                else
                {
                    result.description = this.textBoxDescription.Text;
                }
                cpe.save();

                setTag(cc, result);

                return;
            }

            // multi-row
            int last = this.dataGridView.Rows.Count - 1;
            condition outer = new condition();
            cc.Title = null;
            if (this.listBoxGovernor.SelectedItem.ToString().Equals("all"))
            {
                // = and
                and and = new and();
                outer.Item = and;

                foreach (DataGridViewRow row in this.dataGridView.Rows)
                {
                    // Last row is added automatically
                    if (row == this.dataGridView.Rows[last]) continue;

                    if (row.Cells["Questions"].Value is condition)
                    {
                        // this is just condition re-use!
                        condition cReused = (condition)row.Cells["Questions"].Value;

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(cReused.description); // that'll do for now
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + cReused.description); // that'll do for now
                        }

                        conditionref conditionref = new conditionref();
                        conditionref.id = cReused.id;
                        and.Items.Add(conditionref);

                    }
                    else
                    {
                        // xpathref
                        Pairing pair = buildXPathRef(row);

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(pair.titleText);
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + pair.titleText);
                        }

                        xpathref xpathref = new xpathref();
                        xpathref.id = pair.xpathEntry.id;

                        and.Items.Add(xpathref);
                    }
                }

                outer.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    outer.description = cc.Title;
                }
                else
                {
                    outer.description = this.textBoxDescription.Text;
                }

                cpe.add(outer, null);
                cpe.save();

                setTag(cc, outer);

                return;
            }

            if (this.listBoxGovernor.SelectedItem.ToString().Equals("any") ) {
                // = or
                or or = new or();
                outer.Item = or;

                foreach (DataGridViewRow row in this.dataGridView.Rows)
                {
                    // Last row is added automatically
                    if (row == this.dataGridView.Rows[last]) continue;

                    if (row.Cells["Questions"].Value is condition)
                    {
                        // this is just condition re-use!
                        condition cReused = (condition)row.Cells["Questions"].Value;

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(cReused.description); // that'll do for now
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + cReused.description); // that'll do for now
                        }

                        conditionref conditionref = new conditionref();
                        conditionref.id = cReused.id;
                        or.Items.Add(conditionref);

                    }
                    else
                    {

                        Pairing pair = buildXPathRef(row);

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(pair.titleText);
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " or " + pair.titleText);
                        }

                        xpathref xpathref = new xpathref();
                        xpathref.id = pair.xpathEntry.id;

                        or.Items.Add(xpathref);
                    }
                }

                outer.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    outer.description = cc.Title;
                }
                else
                {
                    outer.description = this.textBoxDescription.Text;
                }

                cpe.add(outer, null);
                cpe.save();

                setTag(cc, outer);

                return;
            }

            if (this.listBoxGovernor.SelectedItem.ToString().Equals("none"))
            {
                // none:  not(A || B) = !A && !B
                not not = new not();
                outer.Item = not;

                or or = new or();
                not.Item = or;

                cc.Title = "NONE OF ";
                foreach (DataGridViewRow row in this.dataGridView.Rows)
                {
                    // Last row is added automatically
                    if (row == this.dataGridView.Rows[last]) continue;
                    if (row.Cells["Questions"].Value is condition)
                    {
                        // this is just condition re-use!
                        condition cReused = (condition)row.Cells["Questions"].Value;

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(cReused.description); // that'll do for now
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + cReused.description); // that'll do for now
                        }

                        conditionref conditionref = new conditionref();
                        conditionref.id = cReused.id;
                        or.Items.Add(conditionref);

                    }
                    else
                    {

                        Pairing pair = buildXPathRef(row);

                        if (cc.Title.Equals("NONE OF "))
                        {
                            cc.Title = this.restrict64chars(cc.Title + pair.titleText);
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " or " + pair.titleText);
                        }

                        xpathref xpathref = new xpathref();
                        xpathref.id = pair.xpathEntry.id;

                        or.Items.Add(xpathref);
                    }
                }

                outer.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    outer.description = cc.Title;
                }
                else
                {
                    outer.description = this.textBoxDescription.Text;
                }

                cpe.add(outer, null);
                cpe.save();

                setTag(cc, outer);

                return;
            }

            //// Make sure this question is allowed here
            //// ie the it is top level or in a common repeat ancestor.
            //// We do this last, so this cc has od:condition on it,
            //// in which case we can re-use existing code to do the check
            //// TODO: when we support and/or, will need to do this
            //// for each variable.
            //ContentControlNewConditionCheck variableRelocator = new ContentControlNewConditionCheck();
            //variableRelocator.checkAnswerAncestry(xpathExisting.id);
        }