Example #1
0
        private void CanonicalizeChildren(XmlElement origElement, XmlElement newElement, NrdoTable type, XmlElement beforeElement, XmlElement afterElement)
        {
            XmlElement preFind        = null;
            bool       parentIsDelete = XmlUtil.GetAttr(newElement, "nrdo.action") == "delete";

            foreach (XmlElement childElement in new List <XmlElement>(Recipe.elementChildren(origElement)))
            {
                string name   = childElement.LocalName;
                string action = XmlUtil.GetAttr(childElement, "nrdo.action");

                // If it names a field whose type is string, grab either the text or the InnerXml of it (depending on the
                // "escaped" attribute) and put it in an attribute on newElement, erroring if such attribute already exists.
                NrdoField field = type.GetField(name);
                if (field != null && field.Type.Equals(typeof(string)))
                {
                    if (newElement.HasAttribute(field.Name))
                    {
                        throw new ArgumentException("Cannot specify " + field.Name + " as both attribute and nested element on " + type.Name);
                    }
                    bool escaped;
                    if (childElement.HasAttribute("escaped"))
                    {
                        switch (XmlUtil.GetAttr(childElement, "escaped"))
                        {
                        case "true":
                            escaped = true;
                            break;

                        case "false":
                            escaped = false;
                            break;

                        default:
                            throw new ArgumentException("escaped attribute must have value 'true' or 'false', not " + XmlUtil.GetAttr(childElement, "escaped"));
                        }
                    }
                    else
                    {
                        escaped = false;
                    }
                    // FIXME: should check for any elements here if (escaped), they're illegal and should be thrown out
                    string value = escaped ? childElement.InnerText : childElement.InnerXml;
                    newElement.SetAttribute(field.Name, value);
                    if (preFind != null)
                    {
                        preFind.SetAttribute(field.Name, value);
                    }
                    continue;
                }

                // If it names an eligible reference, then
                // - CanonicalizeElement(childElement, targetType, beforeElement) and save the result.
                // - If the result doesn't have a nrdo.id, add one from origElement (or error if it doesn't have one)
                // - Add a reference-named attribute to newElement with the nrdo.id of the new element.
                NrdoSingleReference reference = null;
                foreach (NrdoReference testRef in type.References)
                {
                    if (testRef.Name == name && Recipe.isEligibleReference(testRef) &&
                        testRef.AssociatedGet == testRef.TargetTable.PkeyGet && testRef.TargetTable.IsPkeySequenced)
                    {
                        reference = (NrdoSingleReference)testRef;
                        break;
                    }
                }
                if (reference != null)
                {
                    // If the parent element is being deleted, the child element MUST be deleted too.
                    if (parentIsDelete)
                    {
                        if (action != "none" && action != "delete")
                        {
                            throw new ApplicationException(childElement.LocalName + " element inside deleted " + newElement.LocalName + " element must also have nrdo.action='delete'");
                        }
                    }
                    else if (action == "delete")
                    {
                        throw new ApplicationException(childElement.LocalName + " element cannot have nrdo.action='delete' because containing " + newElement.LocalName + " element doesn't");
                    }
                    if (preFind == null)
                    {
                        preFind = newElement.OwnerDocument.CreateElement(newElement.LocalName);
                        foreach (XmlAttribute attr in newElement.Attributes)
                        {
                            preFind.SetAttribute(attr.Name, attr.Value);
                        }
                        if (!parentIsDelete)
                        {
                            preFind.SetAttribute("nrdo.action", "none");
                        }
                        bool insertPreFind = true;
                        if (!preFind.HasAttribute("nrdo.id"))
                        {
                            foreach (NrdoFieldRef pkeyField in type.PkeyGet.Fields)
                            {
                                if (!preFind.HasAttribute(pkeyField.Field.Name))
                                {
                                    insertPreFind = false;
                                }
                            }
                        }
                        if (insertPreFind)
                        {
                            beforeElement.ParentNode.InsertBefore(preFind, beforeElement);
                        }
                        else if (parentIsDelete)
                        {
                            throw new ApplicationException("Cannot construct correct order for deleting " + childElement.LocalName + " without a nrdo.id, either add the right nrdo.id or reorder the elements manually");
                        }
                    }

                    if (!childElement.HasAttribute("nrdo.id"))
                    {
                        if (!newElement.HasAttribute("nrdo.id"))
                        {
                            throw new ArgumentException(childElement.Name + " element must have a nrdo.id attribute.");
                        }
                        childElement.SetAttribute("nrdo.id", XmlUtil.GetAttr(newElement, "nrdo.id") + "_" + reference.Name);
                    }
                    // This needs to be done before canonicalizing, because the canonicalization can actually
                    // result in two records (the child may itself end up turning into a 'preFind') and only
                    // one of these gets returned.
                    if (newElement.HasAttribute("nrdo.id"))
                    {
                        childElement.SetAttribute(reference.Joins[0].To.Field.Name, ":" + XmlUtil.GetAttr(newElement, "nrdo.id") + "." + reference.Joins[0].From.Field.Name);
                    }
                    XmlElement result = CanonicalizeElement(childElement, reference.TargetTable, beforeElement);
                    if (newElement.HasAttribute(reference.Name))
                    {
                        throw new ArgumentException("Cannot specify " + reference.Name + " as both attribute and nested element on " + type.Name);
                    }
                    newElement.SetAttribute(reference.Name, XmlUtil.GetAttr(result, "nrdo.id"));
                    continue;
                }

                // Otherwise it should name a table. If it contains a ".", get the table outright. Otherwise start from
                // the module that "type" is in, trying NrdoTable.GetTable on each until something is found. If nothing is, it's
                // an error.
                NrdoTable table = null;
                if (name.IndexOf('.') >= 0)
                {
                    table = NrdoTable.GetTable(Lookup, name.Replace('.', ':'));
                }
                else
                {
                    string module = type.Module;
                    while (module != null && table == null)
                    {
                        table = NrdoTable.GetTable(Lookup, module + ":" + name);
                        int pos = module.LastIndexOf(':');
                        module = pos < 0 ? null : module.Substring(0, pos);
                    }
                    if (table == null)
                    {
                        table = NrdoTable.GetTable(Lookup, name);
                    }
                }
                if (table == null)
                {
                    throw new ArgumentException("No table called " + name + " found from " + type.Module);
                }

                // Find all eligible references from that table to "type". If there's not exactly one, error. Otherwise take that
                // reference.
                List <NrdoSingleReference> refs = new List <NrdoSingleReference>();
                foreach (NrdoReference testRef in table.References)
                {
                    if (Recipe.isEligibleReference(testRef) && testRef.TargetTable.Name == type.Name)
                    {
                        refs.Add((NrdoSingleReference)testRef);
                    }
                }
                if (refs.Count != 1)
                {
                    throw new ArgumentException("There isn't a single eligible reference from " + table.Name + " to " + type.Name);
                }

                if (parentIsDelete && action != "none" && action != "delete")
                {
                    throw new ApplicationException(childElement.LocalName + " element inside deleted " + newElement.LocalName + " element must also have nrdo.action='delete'");
                }
                else if (parentIsDelete && action == "delete")
                {
                    // FIXME: this needs to somehow turn into <outer nrdo.action="none"/><inner nrdo.action="delete"/><outer nrdo.action="delete"/>
                    throw new ArgumentException("Not yet implemented: cannot use 'back-references as nested element' construct between two elements that are both being deleted (" + newElement.LocalName + " and " + childElement.LocalName + ")");
                }
                else
                {
                    // CanonicalizeElement(childElement, tableType, afterElement) and save the result.
                    // Add a reference-named attribute to the result with the nrdo.id of the current element
                    if (!newElement.HasAttribute("nrdo.id"))
                    {
                        throw new ArgumentException("Cannot use 'back-references as nested element' construct on a " + type.Name + " element without a nrdo.id");
                    }
                    XmlElement refResult = CanonicalizeElement(childElement, table, afterElement);
                    if (refResult.HasAttribute(refs[0].Name))
                    {
                        throw new ArgumentException("Cannot specify " + refs[0].Name + " attribute directly on " + name + " when nesting inside a " + type.Name);
                    }
                    refResult.SetAttribute(refs[0].Name, XmlUtil.GetAttr(newElement, "nrdo.id"));
                }
            }
        }
Example #2
0
 internal object GetValue(NrdoField field)
 {
     return(getValue(field));
 }
Example #3
0
 internal object evaluate(Type type, string expr)
 {
     if (expr.StartsWith(":"))
     {
         if (expr.StartsWith("::"))
         {
             expr = expr.Substring(1);
         }
         else if (expr == ":null")
         {
             return(null);
         }
         else if (expr == ":now")
         {
             if (type.Equals(typeof(DateTime)))
             {
                 return(Now.Value);
             }
             else
             {
                 throw new ArgumentException(":now can only be used on fields of type DateTime, not " + type.FullName);
             }
         }
         else
         {
             string[] parts = expr.Substring(1).Split('.');
             if (parts.Length != 2 || parts[0].Length == 0 || parts[1].Length == 0)
             {
                 throw new ArgumentException("Values that start with : must either be :null, start with :: (for a value that really starts with ':') or be of the form :nrdoid.fieldname. '" + expr + "' is none of these");
             }
             string       nrdoId    = parts[0];
             string       fieldName = parts[1];
             RecipeRecord record    = GetRecord(nrdoId);
             if (record == null)
             {
                 return(Undefined.Value);
             }
             NrdoField field = record.Table.GetField(fieldName);
             if (field == null)
             {
                 throw new ArgumentException("nrdo.id " + nrdoId + " is a " + record.Table.Name + " which does not contain a field called " + fieldName);
             }
             object value = field.Get(record.GetData());
             if (value == null || value.GetType() == type)
             {
                 return(value);
             }
             else
             {
                 try
                 {
                     return(TypeDescriptor.GetConverter(type).ConvertFrom(value));
                 }
                 catch (NotSupportedException)
                 {
                     expr = value.ToString();
                 }
             }
         }
     }
     return(TypeDescriptor.GetConverter(type).ConvertFromString(expr));
 }