// 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); }
// 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); }
// 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); }
// Creates an <xs:schema> 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); }
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); }
// 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. }
// 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. }