CodeNode GenerateAssignment(KeyValuePair <FieldLayoutAttribute, PropertyInfo> pair) { var fieldType = pair.Key.FieldType; StringFieldLayoutAttribute stringLayout = pair.Key as StringFieldLayoutAttribute; ArrayFieldLayoutAttribute arrayLayout = pair.Key as ArrayFieldLayoutAttribute; //if it is an array, defer to GenerateArrayAssignment if (arrayLayout != null) { // TODO: Why do we need these fieldType checks at all? if (fieldType == typeof(string)) { // TODO: Accept string arrays throw new InvalidOperationException(Resources.NoStringArrays); } if (fieldType == typeof(BitArray)) { throw new InvalidOperationException(Resources.NoBitArrayArrays); } return(GenerateArrayAssignment(pair)); } var initNodes = new List <CodeNode>(); bool localWasPresent = true; if (_FieldLocalsTouched.Add(pair.Key.FieldIndex)) { localWasPresent = false; //add a create local node initNodes.Add(new CreateFieldLocalForWritingNode(pair.Key.FieldIndex, pair.Key.FieldType)); _FieldLocalsTouched.Add(pair.Key.FieldIndex); } //find out if there is an optional field flag that we need to manage FieldLayoutAttribute optionalFieldLayout = null; FlaggedFieldLayoutAttribute currentAsOptionalFieldLayout = pair.Key as FlaggedFieldLayoutAttribute; if (currentAsOptionalFieldLayout != null) { optionalFieldLayout = _Fields[currentAsOptionalFieldLayout.FlagIndex].Key; if (_FieldLocalsTouched.Add(currentAsOptionalFieldLayout.FlagIndex)) { initNodes.Add(new CreateFieldLocalForWritingNode(currentAsOptionalFieldLayout.FlagIndex, optionalFieldLayout.FieldType)); } } //this will hold a node that will write in the case we have no value to write CodeNode noValueWriteContingency = null; //this will hold the source of the write that will happen above CodeNode noValueWriteContingencySource = null; //Decide what to do if we don't have a value to write. //This will happen if we don't store the value in a property, it is "missing" from the property source, or something else //TODO: should these have a different precedence? if (pair.Key.MissingValue != null) { //if we have a missing value, set that as the write source noValueWriteContingencySource = new LoadMissingValueNode(pair.Key.MissingValue, fieldType); } else if (localWasPresent || optionalFieldLayout != null) { //if the local was present when we started, that means it was initialized by another field. We can safely write it //Similarly, if this is marked as an optional field, we can still write whatever the value of the local is (cheat) noValueWriteContingencySource = new LoadFieldLocalNode(pair.Key.FieldIndex); } else if (fieldType.IsValueType) { //if T is a value type, we're up a creek with nothing to write. //this is obviously not a good place, so throw in the converter noValueWriteContingency = new ThrowInvalidOperationNode(string.Format(Resources.NonNullableField, pair.Key.FieldIndex, _Type)); } else { //otherwise, we can try to write null (unless it is a fixed-length string, which should have a default instead) if (stringLayout != null && stringLayout.Length > 0) { //TODO: move this check into StdfStringLayout if we can, along with a check that the missing value length matches throw new NotSupportedException(Resources.FixedLengthStringMustHaveDefault); } noValueWriteContingencySource = new LoadNullNode(); } //create the write node and the no-value contingency if we don't already have one CodeNode writeNode; if (stringLayout != null && stringLayout.Length > 0) { noValueWriteContingency = noValueWriteContingency ?? new WriteFixedStringNode(stringLayout.Length, noValueWriteContingencySource); writeNode = new WriteFixedStringNode(stringLayout.Length, new LoadFieldLocalNode(pair.Key.FieldIndex)); } else { noValueWriteContingency = noValueWriteContingency ?? new WriteTypeNode(fieldType, noValueWriteContingencySource); writeNode = new WriteTypeNode(fieldType, new LoadFieldLocalNode(pair.Key.FieldIndex)); } //return the crazy node //TODO: refactor this better, this sucks return(new WriteFieldNode(pair.Key.FieldIndex, fieldType, initialization: new BlockNode(initNodes), sourceProperty: pair.Value, writeOperation: writeNode, noValueWriteContingency: noValueWriteContingency, optionalFieldIndex: optionalFieldLayout == null ? null : (int?)optionalFieldLayout.FieldIndex, optionalFieldMask: optionalFieldLayout == null ? (byte)0 : currentAsOptionalFieldLayout.FlagMask)); }
CodeNode GenerateAssignment(KeyValuePair <FieldLayoutAttribute, PropertyInfo> pair) { var fieldType = pair.Key.FieldType; //if this is an array, defer to GenerateArrayAssignment if (pair.Key is ArrayFieldLayoutAttribute) { // TODO: Why do we need these fieldType checks at all? if (fieldType == typeof(string)) { // TODO: Accept string arrays throw new InvalidOperationException(Resources.NoStringArrays); } if (fieldType == typeof(BitArray)) { throw new InvalidOperationException(Resources.NoBitArrayArrays); } return(GenerateArrayAssignment(pair)); } //get the length if this is a fixed-length string StringFieldLayoutAttribute stringLayout = pair.Key as StringFieldLayoutAttribute; var stringLength = -1; if (stringLayout != null && stringLayout.Length > 0) { stringLength = stringLayout.Length; } //just skip this field if we have an assignment, but shouldn't be parsing it if (pair.Value != null && !ShouldParseField(pair.Value.Name)) { if (stringLength > 0) { return(new SkipRawBytesNode(stringLength)); } else { return(new SkipTypeNode(fieldType)); } } //determine how we'll read the field CodeNode readerNode; if (stringLength > 0) { readerNode = new ReadFixedStringNode(stringLength); } else { readerNode = new ReadTypeNode(fieldType); } BlockNode assignmentBlock = null; //if we have a property to assign to, generate the appropriate assignment statements if (pair.Value != null) { var assignmentNodes = new List <CodeNode>(); //if this is optional, set us up to skip if the missing flag is set FlaggedFieldLayoutAttribute optionalLayout = pair.Key as FlaggedFieldLayoutAttribute; if (optionalLayout != null) { assignmentNodes.Add(new SkipAssignmentIfFlagSetNode(optionalLayout.FlagIndex, optionalLayout.FlagMask)); } //if we have a missing value, set us up to skip if the value matches the missing value else if (pair.Key.MissingValue != null) { if (!(fieldType.IsAssignableFrom(pair.Key.MissingValue.GetType()))) { throw new InvalidOperationException(string.Format("Missing value {0} is not assignable to {1}.", pair.Key.MissingValue, fieldType)); } assignmentNodes.Add(new SkipAssignmentIfMissingValueNode(pair.Key.MissingValue, pair.Key.PersistMissingValue)); } //set us up to assign to the property assignmentNodes.Add(new AssignFieldToPropertyNode(fieldType, pair.Value)); assignmentBlock = new BlockNode(assignmentNodes); } return(new FieldAssignmentNode(fieldType, pair.Key.FieldIndex, readerNode, assignmentBlock)); }