/// <summary> /// Adds an object to DC at parent location. /// The objects content is specified by a sequence of strings where each string /// consists of a pair of attribute name and value. /// </summary> /// <param name="parentDN">The DN of parent.</param> /// <param name="attributes">All attributes of model object.</param> public ModelResult Add(string parentDN, params string[] attributes) { ModelObject parent = TryFindObject(parentDN); if (parent == null) { return(new ModelResult(ResultCode.NoSuchObject, "cannot find '{0}'", parentDN)); } ModelObject obj = new ModelObject(); obj.dc = this; foreach (string line in attributes) { string attr, valueString; string[] splits = line.Split(new char[] { ':' }, 2); if (splits == null || splits.Length != 2) { return(new ModelResult(ResultCode.InvalidAttributeSyntax)); } attr = splits[0].Trim(); valueString = splits[1].Trim(); ModelObject attrObj; if (!TryGetAttribute(splits[0], out attrObj)) { return(new ModelResult(ResultCode.NoSuchAttribute, attr)); } AttributeContext parsingContext = GetAttributeContext(attr); obj[attr] = parsingContext.Parse(valueString); if (Checks.HasDiagnostics) { return(new ModelResult(ResultCode.InvalidAttributeSyntax, Checks.GetAndClearDiagnostics())); } } string rdnName = GetRDNAttributeName(obj); if (rdnName != null) { string rdn = (string)obj[rdnName]; if (parent.childs.ContainsKey(rdn)) { return(new ModelResult(ResultCode.EntryAlreadyExists)); } AddChild(parent, obj); Consistency.Check(new Sequence <ModelObject>(obj)); } if (Checks.HasDiagnostics) { return(new ModelResult(ResultCode.ConstraintViolation, Checks.GetAndClearDiagnostics())); } else { return(ModelResult.Success); } }
/// <summary> /// Adds a set of object definitions to the schema. The format of the schema /// is a sequence of lines where objects are separated by empty lines. /// Each line of an object definition is a pair of an attribute display name and an /// attribute value, separated by ':'. Takes care of "bootstrapping" the contents of /// the schema, resolving cyclic dependencies between attribute definitions and objects. /// </summary> /// <param name="schema">The schema, represented as explained above.</param> /// <param name="valueSubstitution">A substitution of strings which should be applied to each value in the /// schema. For example, may map '<rootdomaindn>' as used in schemas to represent the root name of the /// current DC to an actual value</param> /// <param name="serverVersion">Specify the OS version of the server</param> /// <returns>Result of the Load Schema method.</returns> public ModelResult LoadSchema(IEnumerable<string> schema, Map<string, string> valueSubstitution, OSVersion serverVersion) { SequenceContainer<ModelObject> newObjects = new SequenceContainer<ModelObject>(); UnresolvedSyntax unresolvedSyntax = new UnresolvedSyntax(); #region Pass 1: read in objects resolving only builtin attributes // We need this phase for bootstrapping, as we don't have the // attributes available which are used in the objects. Thus // in this phase, we just parse attribute values of a certain set // of builting attributes which are required to get attribute syntax // definitions. InitializeBuiltinAttributes(); ModelObject tempobj = null; ModelObject obj = null; foreach (string rawLine in schema) { string line = (rawLine == null)?null:rawLine.Trim(); if (String.IsNullOrEmpty(line)) { // Object ends here if (obj != null) { if (obj[StandardNames.attributeID] == null && obj[StandardNames.governsId] == null) { if (obj.attributes.Count == 1) { // If the server is Windows 2000 if (serverVersion == OSVersion.WinSvr2000) { string attr = obj.attributes.Keys.ElementAt(0); if (tempobj.attributes.Keys.Contains(attr)) { tempobj.attributes.Keys.Remove(attr); tempobj.attributes.Add(obj.attributes.ElementAt(0)); } else { tempobj.attributes.Add(obj.attributes.ElementAt(0)); } newObjects.RemoveAt(newObjects.Count - 1); newObjects.Add(tempobj); } } else { Checks.Fail("attributeId/governsId is mandatory for each object {0}.", obj[StandardNames.cn]); } } else if (obj[StandardNames.attributeID] != null) { AddAttribute(obj); newObjects.Add(obj); } else if (obj[StandardNames.governsId] != null) { // This is a class definition. Fill in class map. AddClass(obj); newObjects.Add(obj); } tempobj = obj; obj = null; } } else { if (obj == null) { obj = new ModelObject(); } obj.dc = this; string attr, valueString; string[] splits = line.Split(new char[] { ':' }, 2); if (splits == null || splits.Length != 2) { Checks.Fail("invalid schema line '{0}'", line); continue; } attr = splits[0].Trim().ToLower(); valueString = Substitute(valueSubstitution, splits[1].Trim()); AttributeContext parsingContext; if (!builtinAttributeSyntax.TryGetValue(attr, out parsingContext)) { parsingContext = new AttributeContext(this, attr, unresolvedSyntax); } obj[attr] = parsingContext.Parse(valueString); } } #endregion #region Pass 2: resolve syntax of all new objects // As we have now attribute definitions which map to the syntax, we can parse // All values in the new objects. foreach (ModelObject newObj in newObjects) { foreach (string attr in newObj.attributes.Keys) { AttributeContext parsingContext = GetAttributeContext(attr); Value value = newObj[attr]; if (value.Syntax is UnresolvedSyntax) { newObj[attr] = parsingContext.Parse((string)value); } else { Checks.IsTrue( parsingContext.syntax == value.Syntax, "bultin syntax assignment of '{0}' must match syntax declared in schema", attr); } } } #endregion #region Pass 3: add objects // Now all definitions are complete. Add them to schema replica root. foreach (ModelObject newObj in newObjects) { AddChild(schemaReplica.root, newObj); } #endregion #region Pass 4: check consistency // The tree is finally setup. Now check for consistency Consistency.Check(newObjects.ToSequence()); #endregion if (Checks.HasDiagnostics) { return new ModelResult(ResultCode.ConstraintViolation, Checks.GetAndClearLog(), Checks.GetAndClearDiagnostics()); } else { //return Result.Success; ModelResult tempRes = new ModelResult(ResultCode.Success); tempRes.logMessage = Checks.GetAndClearLog(); return tempRes; } }