public TemplateGenerationMetadata GenerateMetadata()
        {
            var timer = new Stopwatch();
            timer.Start();

            // load the templates we'll be generating into a state storage collection
            var templateData = CreateTemplateData();

            foreach (var template in templateData.Templates)
            {
                HashSet<string> fieldKeys = GetBaseFieldSet(); // get fields on base type

                fieldKeys.Add(template.TypeName); // member names cannot be the same as their enclosing type so we add the type name to the fields collection

                foreach (var baseTemplate in template.Template.AllNonstandardBaseTemplates) // similarly names can't be the same as any of their base templates' names (this would cause an incompletely implemented interface)
                {
                    if (templateData.Contains(baseTemplate.TemplateId))
                    {
                        fieldKeys.Add(templateData[baseTemplate.TemplateId].TypeName);
                    }
                    else fieldKeys.Add(baseTemplate.Name.AsIdentifier()); // NOTE: you could break this if you have a base template called Foo and a field called Foo that IS NOT on the Foo template (but why would you have that?)
                }

                // generate item properties
                foreach (var field in template.Template.Fields)
                {
                    if (_templateInputProvider.IsFieldIncluded(field.Id)) // query the template input provider and make sure the field is included
                    {
                        string propertyName = field.Name.AsNovelIdentifier(fieldKeys);

                        var fieldInfo = new FieldPropertyInfo(field);
                        fieldInfo.FieldPropertyName = propertyName;
                        fieldInfo.SearchFieldName = _indexFieldNameTranslator.GetIndexFieldName(field.Name);
                        fieldInfo.FieldType = _fieldMappingProvider.GetFieldType(field);

                        if (fieldInfo.FieldType == null)
                        {
                            Log.Warn("Synthesis: Field type resolution for " + field.Template.Name + "::" + field.Name + " failed; no mapping found for field type " + field.Type, this);
                            continue; // skip adding the field for generation
                        }

                        // record usage of the property name
                        fieldKeys.Add(propertyName);

                        // add the field to the metadata
                        template.FieldsToGenerate.Add(fieldInfo);
                    }
                }

                // generates interfaces to represent the Sitecore template inheritance hierarchy
                TemplateInfo baseInterface = GenerateInheritedInterfaces(template.Template, templateData);
                if (baseInterface != null)
                    template.InterfacesImplemented.Add(baseInterface);
            }

            timer.Stop();
            Log.Info(string.Format("Synthesis: Generated metadata for {0} concrete templates and {1} interface templates in {2} ms", templateData.Templates.Count, templateData.Interfaces.Count, timer.ElapsedMilliseconds), this);

            return templateData;
        }
        private void CreateItemProperty(FieldPropertyInfo propertyInfo, CodeTypeMemberCollection members)
        {
            Assert.ArgumentNotNull(propertyInfo, "propertyInfo");

            var backingFieldName = "_" + propertyInfo.FieldPropertyName[0].ToString(CultureInfo.InvariantCulture).ToLower() + propertyInfo.FieldPropertyName.Substring(1);
            var backingField = new CodeMemberField(new CodeTypeReference(propertyInfo.FieldType.InternalFieldType), backingFieldName);

            backingField.Attributes = MemberAttributes.Private;

            var property = new CodeMemberProperty
            {
                // ReSharper disable BitwiseOperatorOnEnumWithoutFlags
                Attributes = MemberAttributes.Public | MemberAttributes.Final,
                // ReSharper restore BitwiseOperatorOnEnumWithoutFlags
                Type = new CodeTypeReference(propertyInfo.FieldType.PublicFieldType),
                Name = propertyInfo.FieldPropertyName,
                HasGet = true
            };

            // add [IndexField] attribute
            property.CustomAttributes.Add(GetIndexFieldAttribute(propertyInfo.SearchFieldName));

            // if(backingField == null)
            //	backingField = new SynthesisFieldType(new Lazy<Field>(() => InnerItem.Fields["xxx"], GetSearchFieldValue("index-field-name"));

            var initializerLambda = new CodeSnippetExpression(string.Format("new global::Synthesis.FieldTypes.LazyField(() => InnerItem.Fields[\"{0}\"], \"{1}\", \"{2}\")", propertyInfo.Field.Id, propertyInfo.Field.Template.FullPath, propertyInfo.Field.Name));
            var initializerSearchReference = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(),
                                                                            "GetSearchFieldValue",
                                                                            new CodePrimitiveExpression(propertyInfo.SearchFieldName));

            var backingFieldNullCheck = new CodeConditionStatement();
            backingFieldNullCheck.Condition = new CodeSnippetExpression(string.Format("{0} == null", backingFieldName));
            backingFieldNullCheck.TrueStatements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression(backingFieldName), new CodeObjectCreateExpression(propertyInfo.FieldType.InternalFieldType, initializerLambda, initializerSearchReference)));
            property.GetStatements.Add(backingFieldNullCheck);

            // return backingField;
            property.GetStatements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression(backingFieldName)));

            AddCommentsToFieldProperty(property, propertyInfo.Field);

            members.Add(backingField);
            members.Add(property);
        }
        /// <summary>
        /// Generates an interface for each template that the current template derives from, recursively.
        /// </summary>
        private TemplateInfo GenerateInheritedInterfaces(ITemplateInfo template, TemplateGenerationMetadata templateData)
        {
            var existingInterface = templateData.GetInterface(template.TemplateId);
            if (existingInterface != null) return existingInterface;

            var interfaceInfo = templateData.AddInterface(template, _parameters.InterfaceSuffix);

            var fieldKeys = GetBaseFieldSet();
            fieldKeys.Add(interfaceInfo.TypeName); // member names cannot be the same as their enclosing type
            fieldKeys.Add(template.Name.AsIdentifier()); // prevent any fields from being generated that would conflict with a concrete item name as well as the interface name

            // create interface properties
            foreach (var field in template.OwnFields)
            {
                if (_templateInputProvider.IsFieldIncluded(field.Id))
                {
                    string propertyName = field.Name.AsNovelIdentifier(fieldKeys);

                    var fieldInfo = new FieldPropertyInfo(field);
                    fieldInfo.FieldPropertyName = propertyName;
                    fieldInfo.SearchFieldName = _indexFieldNameTranslator.GetIndexFieldName(field.Name);
                    fieldInfo.FieldType = _fieldMappingProvider.GetFieldType(field);

                    if (fieldInfo.FieldType == null)
                    {
                        Log.Warn("Synthesis: Field type resolution for " + field.Template.Name + "::" + field.Name + " failed; no mapping found for field type " + field.Type, this);
                        continue; // skip adding the field for generation
                    }

                    // record usage of the property name
                    fieldKeys.Add(propertyName);

                    // add the field to the metadata
                    interfaceInfo.FieldsToGenerate.Add(fieldInfo);
                }
            }

            // add base interface inheritance
            foreach (var baseTemplate in template.BaseTemplates)
            {
                if (baseTemplate.Name.ToUpperInvariant() != StandardTemplate)
                {
                    // recursively generate base templates' interfaces as needed
                    var baseInterface = GenerateInheritedInterfaces(baseTemplate, templateData);
                    if (baseInterface != null)
                        interfaceInfo.InterfacesImplemented.Add(baseInterface); // assign interface implementation
                }
            }

            return interfaceInfo;
        }
        private void CreateInterfaceProperty(FieldPropertyInfo propertyInfo, CodeTypeMemberCollection members)
        {
            Assert.ArgumentNotNull(propertyInfo, "propertyInfo");

            var property = new CodeMemberProperty
            {
                Type = new CodeTypeReference(propertyInfo.FieldType.PublicFieldType),
                Name = propertyInfo.FieldPropertyName,
                HasGet = true
            };

            // add [IndexField] attribute
            property.CustomAttributes.Add(GetIndexFieldAttribute(propertyInfo.SearchFieldName));

            AddCommentsToFieldProperty(property, propertyInfo.Field);

            members.Add(property);
        }