예제 #1
0
        //  Merges the tree of Elements whose root is <code>childElement</code>
        //  underneath the <code>parentElement</code>.
        //
        //  If the <code>parentElement</code> already has an element that matches
        //  the <code>childElement</code>, then recursively attaches the
        //  grandchildren instead.
        //
        //  The element returned will be either the supplied
        //  <code>childElement</code>, or an existing child element if one already
        //  existed under <code>parentElement</code>.

        private static XElement MergeTree(XElement parentElement, XElement childElement)
        {
            var childElementOid = NofMetaModel.GetAttribute(childElement, "oid");

            if (childElementOid != null)
            {
                // before we add the child element, check to see if it is already
                // there
                var existingChildElements = ElementsUnder(parentElement, childElement.Name.LocalName);
                foreach (var possibleMatchingElement in existingChildElements)
                {
                    var possibleMatchOid = NofMetaModel.GetAttribute(possibleMatchingElement, "oid");
                    if (possibleMatchOid == null || !possibleMatchOid.Equals(childElementOid))
                    {
                        continue;
                    }

                    // match: transfer the children of the child (grandchildren) to the
                    // already existing matching child
                    var existingChildElement  = possibleMatchingElement;
                    var grandchildrenElements = ElementsUnder(childElement, "*");
                    foreach (var grandchildElement in grandchildrenElements)
                    {
                        grandchildElement.Remove();

                        MergeTree(existingChildElement, grandchildElement);
                    }

                    return(existingChildElement);
                }
            }

            parentElement.Add(childElement);
            return(childElement);
        }
예제 #2
0
        // The prefix to the namespace for the application.

        // Creates an element with the specified localName, in the appropriate namespace for the NOS.
        //
        // If necessary the namespace definition is added to the root element of the doc used to
        // create the element.  The element is not parented but to avoid an error can only be added
        // as a child of another element in the same doc.

        public XElement CreateElement(XDocument doc, string localName, string fullyQualifiedClassName, string singularName, string pluralName)
        {
            XNamespace nsUri = GetUri();

            var element = new XElement(nsUri + localName);

            element.SetAttributeValue(NofMetaModel.Nof + "fqn", fullyQualifiedClassName);
            element.SetAttributeValue(NofMetaModel.Nof + "singular", singularName);
            element.SetAttributeValue(NofMetaModel.Nof + "plural", pluralName);
            NofMetaModel.AddNamespace(element); // good a place as any

            AddNamespace(element, Prefix, GetUri());
            return(element);
        }
예제 #3
0
        //  Merges the tree of Elements whose root is <code>childElement</code>
        //  underneath the <code>parentElement</code>.
        //
        //  If the <code>parentElement</code> already has an element that matches
        //  the <code>childElement</code>, then recursively attaches the
        //  grandchildren instead.
        //
        //  The element returned will be either the supplied
        //  <code>childElement</code>, or an existing child element if one already
        //  existed under <code>parentElement</code>.

        private static XElement MergeTree(XElement parentElement, XElement childElement)
        {
            Log.Debug("mergeTree(" + DoLog("parent", parentElement) + AndLog("child", childElement));

            string childElementOid = NofMetaModel.GetAttribute(childElement, "oid");

            Log.Debug("mergeTree(El,El): " + DoLog("childOid", childElementOid));
            if (childElementOid != null)
            {
                // before we add the child element, check to see if it is already
                // there
                Log.Debug("mergeTree(El,El): check if child already there");
                IEnumerable <XElement> existingChildElements = ElementsUnder(parentElement, childElement.Name.LocalName);
                foreach (XElement possibleMatchingElement in existingChildElements)
                {
                    string possibleMatchOid = NofMetaModel.GetAttribute(possibleMatchingElement, "oid");
                    if (possibleMatchOid == null || !possibleMatchOid.Equals(childElementOid))
                    {
                        continue;
                    }

                    Log.Debug("mergeTree(El,El): child already there; merging grandchildren");

                    // match: transfer the children of the child (grandchildren) to the
                    // already existing matching child
                    XElement existingChildElement = possibleMatchingElement;
                    IEnumerable <XElement> grandchildrenElements = ElementsUnder(childElement, "*");
                    foreach (XElement grandchildElement in grandchildrenElements)
                    {
                        grandchildElement.Remove();

                        Log.Debug("mergeTree(El,El): merging " + DoLog("grandchild", grandchildElement));

                        MergeTree(existingChildElement, grandchildElement);
                    }
                    return(existingChildElement);
                }
            }

            parentElement.Add(childElement);
            return(childElement);
        }
예제 #4
0
        // Creates an &lt;xs:schema&gt; element for the document
        // to the provided element, attaching to root of supplied Xsd doc.
        //
        // In addition:
        //  - the elementFormDefault is set
        //  - the NOF namespace is set
        //  - the <code>xs:import</code> element referencing the NOF namespace is added  as a child

        public static XElement CreateXsSchemaElement(XDocument xsdDoc)
        {
            if (xsdDoc.Root != null)
            {
                throw new ArgumentException("XSD document already has content");
            }
            XElement xsSchemaElement = CreateXsElement(xsdDoc, "schema");

            xsSchemaElement.SetAttributeValue("elementFormDefault", "qualified");

            NofMetaModel.AddNamespace(xsSchemaElement);

            xsdDoc.Add(xsSchemaElement);
            XElement xsImportElement = CreateXsElement(xsdDoc, "import");

            xsImportElement.SetAttributeValue("namespace", NofMetaModel.Nof.NamespaceName);
            xsImportElement.SetAttributeValue("schemaLocation", NofMetaModel.DefaultNofSchemaLocation);

            xsSchemaElement.Add(xsImportElement);

            return(xsSchemaElement);
        }
예제 #5
0
        public Place ObjectToElement(INakedObjectAdapter nakedObjectAdapter)
        {
            var nos = (IObjectSpec)nakedObjectAdapter.Spec;

            var element = Schema.CreateElement(XmlDocument, nos.ShortName, nos.FullName, nos.SingularName, nos.PluralName);

            NofMetaModel.AppendNofTitle(element, nakedObjectAdapter.TitleString());

            var xsElement = Schema.CreateXsElementForNofClass(XsdDocument, element, topLevelElementWritten);

            // hack: every element in the XSD schema apart from first needs minimum cardinality setting.
            topLevelElementWritten = true;

            var place = new Place(nakedObjectAdapter, element);

            NofMetaModel.SetAttributesForClass(element, OidOrHashCode(nakedObjectAdapter));

            var fields = nos.Properties;

            var seenFields = new List <string>();

            foreach (var field in fields)
            {
                var fieldName = field.Id;

                // Skip field if we have seen the name already
                // This is a workaround for getLastActivity(). This method exists
                // in AbstractNakedObject, but is not (at some level) being picked up
                // by the dot-net reflector as a property. On the other hand it does
                // exist as a field in the meta model (NakedObjectSpecification).
                //
                // Now, to re-expose the LastActivity field for .Net, a deriveLastActivity()
                // has been added to BusinessObject. This caused another field of the
                // same name, ultimately breaking the XSD.

                if (seenFields.Contains(fieldName))
                {
                    continue;
                }

                seenFields.Add(fieldName);

                XNamespace ns = Schema.GetUri();

                var xmlFieldElement = new XElement(ns + fieldName);

                XElement xsdFieldElement;
                var      oneToOneAssociation  = field as IOneToOneAssociationSpec;
                var      oneToManyAssociation = field as IOneToManyAssociationSpec;

                if (field.ReturnSpec.IsParseable && oneToOneAssociation != null)
                {
                    var fieldNos = field.ReturnSpec;
                    // skip fields of type XmlValue
                    if (fieldNos?.FullName != null && fieldNos.FullName.EndsWith("XmlValue"))
                    {
                        continue;
                    }

                    var xmlValueElement = xmlFieldElement; // more meaningful locally scoped name

                    try {
                        var value = oneToOneAssociation.GetNakedObject(nakedObjectAdapter);

                        // a null value would be a programming error, but we protect
                        // against it anyway
                        if (value == null)
                        {
                            continue;
                        }

                        var valueNos = value.Spec;

                        // XML
                        NofMetaModel.SetAttributesForValue(xmlValueElement, valueNos.ShortName);

                        var notEmpty = value.TitleString().Length > 0;
                        if (notEmpty)
                        {
                            var valueStr = value.TitleString();
                            xmlValueElement.Add(new XText(valueStr));
                        }
                        else
                        {
                            NofMetaModel.SetIsEmptyAttribute(xmlValueElement, true);
                        }
                    }
                    catch (Exception) {
                        logger.LogWarning($"objectToElement(NO): {DoLog("field", fieldName)}: getField() threw exception - skipping XML generation");
                    }

                    // XSD
                    xsdFieldElement = Schema.CreateXsElementForNofValue(xsElement, xmlValueElement);
                }
                else if (oneToOneAssociation != null)
                {
                    var xmlReferenceElement = xmlFieldElement; // more meaningful locally scoped name

                    try {
                        var referencedNakedObjectAdapter = oneToOneAssociation.GetNakedObject(nakedObjectAdapter);
                        var fullyQualifiedClassName      = field.ReturnSpec.FullName;

                        // XML
                        NofMetaModel.SetAttributesForReference(xmlReferenceElement, Schema.Prefix, fullyQualifiedClassName);

                        if (referencedNakedObjectAdapter != null)
                        {
                            NofMetaModel.AppendNofTitle(xmlReferenceElement, referencedNakedObjectAdapter.TitleString());
                        }
                        else
                        {
                            NofMetaModel.SetIsEmptyAttribute(xmlReferenceElement, true);
                        }
                    }
                    catch (Exception) {
                        logger.LogWarning($"objectToElement(NO): {DoLog("field", fieldName)}: getAssociation() threw exception - skipping XML generation");
                    }

                    // XSD
                    xsdFieldElement = Schema.CreateXsElementForNofReference(xsElement, xmlReferenceElement, oneToOneAssociation.ReturnSpec.FullName);
                }
                else if (oneToManyAssociation != null)
                {
                    var xmlCollectionElement = xmlFieldElement; // more meaningful locally scoped name

                    try {
                        var collection = oneToManyAssociation.GetNakedObject(nakedObjectAdapter);
                        var facet      = collection.GetTypeOfFacetFromSpec();

                        var referencedTypeNos       = facet.GetValueSpec(collection, metamodelManager.Metamodel);
                        var fullyQualifiedClassName = referencedTypeNos.FullName;

                        // XML
                        NofMetaModel.SetNofCollection(xmlCollectionElement, Schema.Prefix, fullyQualifiedClassName, collection, nakedObjectManager);
                    }
                    catch (Exception) {
                        logger.LogWarning($"objectToElement(NO): {DoLog("field", fieldName)}: get(obj) threw exception - skipping XML generation");
                    }

                    // XSD
                    xsdFieldElement = Schema.CreateXsElementForNofCollection(xsElement, xmlCollectionElement, oneToManyAssociation.ReturnSpec.FullName);
                }
                else
                {
                    continue;
                }

                if (xsdFieldElement != null)
                {
                    xmlFieldElement.AddAnnotation(xsdFieldElement);
                }

                // XML
                MergeTree(element, xmlFieldElement);

                // XSD
                if (xsdFieldElement != null)
                {
                    Schema.AddFieldXsElement(xsElement, xsdFieldElement);
                }
            }

            return(place);
        }
예제 #6
0
        //  return true if able to navigate the complete vector of field names
        //                  successfully; false if a field could not be located or it turned
        //                  out to be a value.

        private bool IncludeField(Place place, IList <string> fieldNames, string annotation)
        {
            var nakedObjectAdapter = place.NakedObjectAdapter;
            var xmlElement         = place.XmlElement;

            // we use a copy of the path so that we can safely traverse collections
            // without side-effects
            fieldNames = fieldNames.ToList();

            // see if we have any fields to process
            if (!fieldNames.Any())
            {
                return(true);
            }

            // take the first field name from the list, and remove
            var fieldName = fieldNames.First();

            fieldNames.Remove(fieldName);

            // locate the field in the object's class
            var nos   = (IObjectSpec)nakedObjectAdapter.Spec;
            var field = nos.Properties.SingleOrDefault(p => p.Id.ToLower() == fieldName);

            if (field == null)
            {
                return(false);
            }

            // locate the corresponding XML element
            // (the corresponding XSD element will later be attached to xmlElement
            // as its userData)
            var xmlFieldElements = ElementsUnder(xmlElement, field.Id).ToArray();
            var fieldCount       = xmlFieldElements.Length;

            if (fieldCount != 1)
            {
                return(false);
            }

            var xmlFieldElement = xmlFieldElements.First();

            if (!fieldNames.Any() && annotation != null)
            {
                // nothing left in the path, so we will apply the annotation now
                NofMetaModel.SetAnnotationAttribute(xmlFieldElement, annotation);
            }

            var fieldPlace = new Place(nakedObjectAdapter, xmlFieldElement);

            if (field.ReturnSpec.IsParseable)
            {
                return(false);
            }

            var oneToOneAssociation = field as IOneToOneAssociationSpec;

            if (oneToOneAssociation != null)
            {
                var referencedObjectAdapter = oneToOneAssociation.GetNakedObject(fieldPlace.NakedObjectAdapter);

                if (referencedObjectAdapter == null)
                {
                    return(true); // not a failure if the reference was null
                }

                var appendedXml = AppendXmlThenIncludeRemaining(fieldPlace, referencedObjectAdapter, fieldNames, annotation);

                return(appendedXml);
            }

            var oneToManyAssociation = field as IOneToManyAssociationSpec;

            if (oneToManyAssociation != null)
            {
                var collection = oneToManyAssociation.GetNakedObject(fieldPlace.NakedObjectAdapter);

                var collectionAsEnumerable = collection.GetAsEnumerable(nakedObjectManager).ToArray();

                var allFieldsNavigated = true;

                foreach (var referencedObject in collectionAsEnumerable)
                {
                    var appendedXml = AppendXmlThenIncludeRemaining(fieldPlace, referencedObject, fieldNames, annotation);

                    allFieldsNavigated = allFieldsNavigated && appendedXml;
                }

                return(allFieldsNavigated);
            }

            return(false); // fall through, shouldn't get here but just in case.
        }
예제 #7
0
        //  return true if able to navigate the complete vector of field names
        //                  successfully; false if a field could not be located or it turned
        //                  out to be a value.

        private bool IncludeField(Place place, IList <string> fieldNames, string annotation)
        {
            Log.DebugFormat("includeField(: {0})", DoLog("place", place) + AndLog("fieldNames", fieldNames) + AndLog("annotation", annotation));

            INakedObject nakedObject = place.NakedObject;
            XElement     xmlElement  = place.XmlElement;

            // we use a copy of the path so that we can safely traverse collections
            // without side-effects
            fieldNames = fieldNames.ToList();

            // see if we have any fields to process
            if (!fieldNames.Any())
            {
                return(true);
            }

            // take the first field name from the list, and remove
            string fieldName = fieldNames.First();

            fieldNames.Remove(fieldName);

            Log.Debug("includeField(Pl, Vec, Str):" + DoLog("processing field", fieldName) + AndLog("left", "" + fieldNames.Count()));

            // locate the field in the object's class
            IObjectSpec      nos   = (IObjectSpec)nakedObject.Spec;
            IAssociationSpec field = nos.Properties.SingleOrDefault(p => p.Id.ToLower() == fieldName);

            if (field == null)
            {
                Log.Info("includeField(Pl, Vec, Str): could not locate field, skipping");
                return(false);
            }

            // locate the corresponding XML element
            // (the corresponding XSD element will later be attached to xmlElement
            // as its userData)
            Log.Debug("includeField(Pl, Vec, Str): locating corresponding XML element");
            XElement[] xmlFieldElements = ElementsUnder(xmlElement, field.Id).ToArray();
            int        fieldCount       = xmlFieldElements.Count();

            if (fieldCount != 1)
            {
                Log.Info("includeField(Pl, Vec, Str): could not locate " + DoLog("field", field.Id) + AndLog("xmlFieldElements.size", "" + fieldCount));
                return(false);
            }
            XElement xmlFieldElement = xmlFieldElements.First();

            if (!fieldNames.Any() && annotation != null)
            {
                // nothing left in the path, so we will apply the annotation now
                NofMetaModel.SetAnnotationAttribute(xmlFieldElement, annotation);
            }

            var fieldPlace = new Place(nakedObject, xmlFieldElement);

            if (field.ReturnSpec.IsParseable)
            {
                Log.Debug("includeField(Pl, Vec, Str): field is value; done");
                return(false);
            }
            var oneToOneAssociation = field as IOneToOneAssociationSpec;

            if (oneToOneAssociation != null)
            {
                Log.Debug("includeField(Pl, Vec, Str): field is 1->1");

                INakedObject referencedObject = oneToOneAssociation.GetNakedObject(fieldPlace.NakedObject);

                if (referencedObject == null)
                {
                    return(true); // not a failure if the reference was null
                }

                bool appendedXml = AppendXmlThenIncludeRemaining(fieldPlace, referencedObject, fieldNames, annotation);

                Log.Debug("includeField(Pl, Vec, Str): 1->1: invoked appendXmlThenIncludeRemaining for " + DoLog("referencedObj", referencedObject) + AndLog("returned", "" + appendedXml));

                return(appendedXml);
            }
            var oneToManyAssociation = field as IOneToManyAssociationSpec;

            if (oneToManyAssociation != null)
            {
                Log.Debug("includeField(Pl, Vec, Str): field is 1->M");

                INakedObject collection = oneToManyAssociation.GetNakedObject(fieldPlace.NakedObject);

                INakedObject[] collectionAsEnumerable = collection.GetAsEnumerable(nakedObjectManager).ToArray();

                Log.Debug("includeField(Pl, Vec, Str): 1->M: " + DoLog("collection.size", "" + collectionAsEnumerable.Count()));
                bool allFieldsNavigated = true;

                foreach (INakedObject referencedObject in collectionAsEnumerable)
                {
                    bool appendedXml = AppendXmlThenIncludeRemaining(fieldPlace, referencedObject, fieldNames, annotation);

                    Log.Debug("includeField(Pl, Vec, Str): 1->M: + invoked appendXmlThenIncludeRemaining for " + DoLog("referencedObj", referencedObject) + AndLog("returned", "" + appendedXml));

                    allFieldsNavigated = allFieldsNavigated && appendedXml;
                }
                Log.Debug("includeField(Pl, Vec, Str): " + DoLog("returning", "" + allFieldsNavigated));

                return(allFieldsNavigated);
            }

            return(false); // fall through, shouldn't get here but just in case.
        }