private void DispatchLoadToExtensions() { if (Hierarchy != null) { var projectItem = VsUtils.GetProjectItem(Hierarchy, ItemId); if (projectItem != null) { var fileContents = VSHelpers.GetTextFromVsTextLines(VsBuffer); string newBufferContents; List <ExtensionError> extensionErrors; if (StandaloneXmlModelProvider.TryGetBufferViaExtensions( projectItem, fileContents, out newBufferContents, out extensionErrors)) { if (VSHelpers.CheckOutFilesIfEditable(ServiceProvider, new[] { FileName })) { VsUtils.SetTextForVsTextLines(VsBuffer, newBufferContents); } else { ErrorListHelper.LogExtensionErrors( new List <ExtensionError> { new ExtensionError( string.Format( CultureInfo.CurrentCulture, Resources.ExtensionError_SourceControlLock, Path.GetFileName(FileName)), ErrorCodes.ExtensionsError_BufferNotEditable, ExtensionErrorSeverity.Error) }, projectItem); } } } } }
internal string DispatchSaveToExtensions( IServiceProvider serviceProvider, ProjectItem projectItem, string fileContents, Lazy <IModelConversionExtension, IEntityDesignerConversionData>[] converters, Lazy <IModelTransformExtension>[] serializers) { Debug.Assert(projectItem != null, "projectItem != null"); Debug.Assert(fileContents != null, "bufferText != null"); Debug.Assert(serializers != null && converters != null, "extensions must not be null"); Debug.Assert(serializers.Any() || converters.Any(), "at least one extension expected"); ModelTransformContextImpl transformContext = null; ModelConversionContextImpl conversionContext = null; try { var original = XDocument.Parse(fileContents, LoadOptions.PreserveWhitespace); var targetSchemaVersion = EdmUtils.GetEntityFrameworkVersion(projectItem.ContainingProject, serviceProvider); Debug.Assert(targetSchemaVersion != null, "we should not get here for Misc projects"); transformContext = new ModelTransformContextImpl(projectItem, targetSchemaVersion, original); // call the extensions that can save EDMX files first (even if we aren't going to end up in an EDMX file, let them process) VSArtifact.DispatchToSerializationExtensions(serializers, transformContext, loading: false); // get the extension of the file being loaded (might not be EDMX); this API will include the preceeding "." var fileInfo = new FileInfo(FileName); var fileExtension = fileInfo.Extension; // now if this is not an EDMX file, hand off to the extension who can convert it to the writable content if (!string.Equals(fileExtension, EntityDesignArtifact.ExtensionEdmx, StringComparison.OrdinalIgnoreCase)) { // the current document coming from the serializers becomes our original conversionContext = new ModelConversionContextImpl( projectItem.ContainingProject, projectItem, fileInfo, targetSchemaVersion, transformContext.CurrentDocument); // we aren't loading an EDMX file, so call the extensions who can process this file extension // when this finishes, then output should be a valid EDMX document VSArtifact.DispatchToConversionExtensions(converters, fileExtension, conversionContext, false); // we are done saving, so get bufferText from the OriginalDocument // TODO use Utf8StringWriter here somehow? return(conversionContext.OriginalDocument); } else { // we are saving an EDMX file, so get bufferText from the XDocument using (var writer = new Utf8StringWriter()) { transformContext.CurrentDocument.Save(writer, SaveOptions.None); return(writer.ToString()); } } } catch (XmlException) { // Don't do anything here. We will want to gracefully step out of the extension loading // and let the core designer handle this. return(fileContents); } finally { var errorList = ErrorListHelper.GetExtensionErrorList(serviceProvider); errorList.Clear(); // log any errors if (conversionContext != null) { if (conversionContext.Errors.Count > 0) { ErrorListHelper.LogExtensionErrors(conversionContext.Errors, projectItem); } conversionContext.Dispose(); } if (transformContext != null) { if (transformContext.Errors.Count > 0) { ErrorListHelper.LogExtensionErrors(transformContext.Errors, projectItem); } transformContext.Dispose(); } } }
public bool CreateAndLoadBuffer() { DestroyBuffer(); var pkg = PackageManager.Package as Package; IServiceProvider serviceProvider = pkg; var textLinesType = typeof(VSTextManagerInterop.IVsTextLines); var riid = textLinesType.GUID; var clsid = typeof(VSTextManagerInterop.VsTextBufferClass).GUID; _underlyingBuffer = pkg.CreateInstance(ref clsid, ref riid, textLinesType); Debug.Assert(_underlyingBuffer != null, "Failure while creating buffer."); var buffer = _underlyingBuffer as VSTextManagerInterop.IVsTextLines; Debug.Assert(buffer != null, "Why does buffer not implement IVsTextLines?"); var ows = buffer as IObjectWithSite; if (ows != null) { ows.SetSite(serviceProvider.GetService(typeof(IOleServiceProvider))); } // We want to set the LanguageService SID explicitly to the XML Language Service. // We need turn off GUID_VsBufferDetectLangSID before calling LoadDocData so that the // TextBuffer does not do the work to detect the LanguageService SID from the file extension. var userData = buffer as VSTextManagerInterop.IVsUserData; if (userData != null) { var VsBufferDetectLangSID = new Guid("{17F375AC-C814-11d1-88AD-0000F87579D2}"); //GUID_VsBufferDetectLangSID; VSErrorHandler.ThrowOnFailure(userData.SetData(ref VsBufferDetectLangSID, false)); } var langSid = CommonPackageConstants.xmlEditorLanguageService; VSErrorHandler.ThrowOnFailure(buffer.SetLanguageServiceID(ref langSid)); var persistDocData = buffer as IVsPersistDocData; if (persistDocData != null) { persistDocData.LoadDocData(FileName); var artifactUri = new Uri(FileName); var artifact = PackageManager.Package.ModelManager.GetArtifact(artifactUri); if (artifact != null && artifact.IsCodeGenArtifact) { var standaloneProvider = artifact.XmlModelProvider as StandaloneXmlModelProvider; if (standaloneProvider.ExtensionErrors == null || standaloneProvider.ExtensionErrors.Count == 0) { // If there is a cached code gen artifact, it will have loaded its text buffer using extensions already. // Therefore we can grab the text buffer from that artifact for our docdata buffer, and dispose the // code gen artifact since it's using a XmlProvider that is standalone and won't be supported by the // designer. var projectItem = VsUtils.GetProjectItem(Hierarchy, ItemId); if (projectItem != null) { if (VSHelpers.CheckOutFilesIfEditable(ServiceProvider, new[] { FileName })) { string artifactText = null; using (var writer = new Utf8StringWriter()) { artifact.XDocument.Save(writer, SaveOptions.None); artifactText = writer.ToString(); } if (!String.IsNullOrWhiteSpace(artifactText)) { VsUtils.SetTextForVsTextLines(VsBuffer, artifactText); } } else { ErrorListHelper.LogExtensionErrors( new List <ExtensionError> { new ExtensionError( string.Format( CultureInfo.CurrentCulture, Resources.ExtensionError_SourceControlLock, Path.GetFileName(FileName)), ErrorCodes.ExtensionsError_BufferNotEditable, ExtensionErrorSeverity.Error) }, projectItem); } } PackageManager.Package.ModelManager.ClearArtifact(artifactUri); } else { // If the extensions ran into errors whilst loading, we'll need to re-run extensions anyway so we ignore the cache PackageManager.Package.ModelManager.ClearArtifact(artifactUri); DispatchLoadToExtensions(); } } else { DispatchLoadToExtensions(); } } // DSL exposes the FileNameChanged event which we subscribe to so we can update the moniker inside // the text buffer, which is required to update our model as well as keep the XmlModel in sync. FileNameChanged += OnFileNameChanged; RegisterUndoTracking(); return(true); }
internal static bool TryGetBufferViaExtensions( IServiceProvider serviceProvider, ProjectItem projectItem, string fileContents, Lazy <IModelConversionExtension, IEntityDesignerConversionData>[] converters, Lazy <IModelTransformExtension>[] serializers, out string documentViaExtensions, out List <ExtensionError> errors) { Debug.Assert(serviceProvider != null, "serviceProvider != null"); Debug.Assert(projectItem != null, "projectItem != null"); Debug.Assert(VsUtils.EntityFrameworkSupportedInProject(projectItem.ContainingProject, serviceProvider, false)); Debug.Assert(serializers != null && converters != null, "extensions must not be null"); Debug.Assert(serializers.Any() || converters.Any(), "at least one extension expected"); errors = new List <ExtensionError>(); documentViaExtensions = ""; ModelConversionContextImpl conversionContext = null; ModelTransformContextImpl transformContext = null; try { var targetSchemaVersion = EdmUtils.GetEntityFrameworkVersion(projectItem.ContainingProject, serviceProvider); Debug.Assert(targetSchemaVersion != null, "should not get here for a Misc project"); // get the extension of the file being loaded (might not be EDMX); this API will include the preceding "." var filePath = projectItem.get_FileNames(1); var fileExtension = Path.GetExtension(filePath); XDocument originalDocument = null; // see if we are loading an EDMX file or not, and if we have any converters if (!string.Equals( fileExtension, EntityDesignArtifact.ExtensionEdmx, StringComparison.OrdinalIgnoreCase)) { conversionContext = new ModelConversionContextImpl( projectItem.ContainingProject, projectItem, new FileInfo(filePath), targetSchemaVersion, fileContents); // we aren't loading an EDMX file, so call the extensions who can process this file extension // when this finishes, then output should be a valid EDMX document VSArtifact.DispatchToConversionExtensions(converters, fileExtension, conversionContext, true); // we are done with the non-EDMX extensions so CurrentDocument will be a valid EDMX document // create the serialization context for further extensions to act on transformContext = new ModelTransformContextImpl( projectItem, targetSchemaVersion, conversionContext.CurrentDocument); } else { // we are loading an EDMX file, we can parse file contents into an XDocument try { originalDocument = XDocument.Parse(fileContents, LoadOptions.PreserveWhitespace); transformContext = new ModelTransformContextImpl( projectItem, targetSchemaVersion, originalDocument); } catch (XmlException) { // If there's an error here, don't do anything. We will want to gracefully step out of the extension loading // since the designer itself won't load. } } if (transformContext != null && originalDocument != null) { // now dispatch to those that want to work on EDMX files VSArtifact.DispatchToSerializationExtensions(serializers, transformContext, true); // TODO: this does not seem to be correct if severity is Message or Warning if (transformContext.Errors.Count == 0) { // see if any extension changed things. Note that we need to compare the serialization of // the XDocuments together since the original buffer may have different whitespace after creating the XDocument. // TODO: Why not use XNode.DeepEquals()? string newBufferContents; using (var currentDocWriter = new Utf8StringWriter()) { transformContext.CurrentDocument.Save(currentDocWriter, SaveOptions.None); newBufferContents = currentDocWriter.ToString(); } string originalBufferContents; using (var originalDocWriter = new Utf8StringWriter()) { originalDocument.Save(originalDocWriter, SaveOptions.None); originalBufferContents = originalDocWriter.ToString(); } if (!string.Equals(originalBufferContents, newBufferContents, StringComparison.Ordinal)) { documentViaExtensions = newBufferContents; return(true); } } else { errors.AddRange(transformContext.Errors); return(false); } } } finally { var errorList = ErrorListHelper.GetExtensionErrorList(serviceProvider); errorList.Clear(); // log any errors if (conversionContext != null && conversionContext.Errors.Count > 0) { ErrorListHelper.LogExtensionErrors(conversionContext.Errors, projectItem); } if (transformContext != null && transformContext.Errors.Count > 0) { ErrorListHelper.LogExtensionErrors(transformContext.Errors, projectItem); } } return(false); }