From: <mcu...@us...> - 2007-09-10 23:46:56
|
Revision: 1124 http://orm.svn.sourceforge.net/orm/?rev=1124&view=rev Author: mcurland Date: 2007-09-10 16:46:57 -0700 (Mon, 10 Sep 2007) Log Message: ----------- Added ability to allow generators to require extensions from the ORM designer. The UI is a little clunky (you need to open the ORM designer to add extensions), but the large amount of additional work to do this without opening the designer file coupled with the small usability gain makes the designer-free extension enabling a low priority feature. refs #339 Modified Paths: -------------- trunk/ORMModel/Shell/ExtensionManager.cs trunk/ORMModel/Shell/ORMDocData.cs trunk/Tools/ORMCustomTool/Extender.cs trunk/Tools/ORMCustomTool/IORMGenerator.cs trunk/Tools/ORMCustomTool/ORMCustomTool.cs trunk/Tools/ORMCustomTool/ORMCustomTool.csproj trunk/Tools/ORMCustomTool/UI/MainBranch.cs trunk/Tools/ORMCustomTool/UI/ORMGeneratorSelectionControl.cs trunk/Tools/ORMCustomTool/XslORMGenerator.cs Added Paths: ----------- trunk/Tools/ORMCustomTool/ORMExtensionManager.cs Modified: trunk/ORMModel/Shell/ExtensionManager.cs =================================================================== --- trunk/ORMModel/Shell/ExtensionManager.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/ORMModel/Shell/ExtensionManager.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -77,47 +77,8 @@ checkedTypes.Add((ORMExtensionType)listViewItem.Tag); } - IList<ORMExtensionType> allExtensionTypes = extensionManager._allExtensionTypes; + AddRequiredExtensions(checkedTypes, extensionManager._allExtensionTypes); - List<Guid> loadedDomainModelIds = new List<Guid>(4 + checkedTypes.Count); - loadedDomainModelIds.Add(CoreDomainModel.DomainModelId); - loadedDomainModelIds.Add(Microsoft.VisualStudio.Modeling.Diagrams.CoreDesignSurfaceDomainModel.DomainModelId); - loadedDomainModelIds.Add(ObjectModel.ORMCoreDomainModel.DomainModelId); - loadedDomainModelIds.Add(ShapeModel.ORMShapeDomainModel.DomainModelId); - foreach (ORMExtensionType extensionType in checkedTypes) - { - loadedDomainModelIds.Add(extensionType.Type.GUID); - } - - for (int i = 0; i < checkedTypes.Count; i++) - { - foreach (ExtendsDomainModelAttribute extendsDomainModelAttribute in checkedTypes[i].Type.GetCustomAttributes(typeof(ExtendsDomainModelAttribute), false)) - { - Guid extendedModelId = extendsDomainModelAttribute.ExtendedModelId; - if (!loadedDomainModelIds.Contains(extendedModelId)) - { - // Find the missing domain model - foreach (ORMExtensionType candidateExtensionType in allExtensionTypes) - { - object[] domainObjectIdAttributes = candidateExtensionType.Type.GetCustomAttributes(typeof(DomainObjectIdAttribute), false); - if (domainObjectIdAttributes.Length <= 0) - { - continue; - } - Guid candidateModelId = ((DomainObjectIdAttribute)domainObjectIdAttributes[0]).Id; - if (extendedModelId.Equals(candidateModelId)) - { - loadedDomainModelIds.Add(candidateModelId); - checkedTypes.Add(candidateExtensionType); - break; - } - } - // If we didn't find the requested domain model, we don't need to worry about doing anything here, - // since the Store will throw later when they try to load the requesting domain model. - } - } - } - Stream stream = null; try { @@ -140,6 +101,50 @@ } } /// <summary> + /// Given a current list of extensions and all available extensions, add additional required extensions to the list + /// </summary> + public static void AddRequiredExtensions(IList<ORMExtensionType> extensions, IList<ORMExtensionType> allExtensions) + { + Dictionary<Guid, object> loadedDomainModelIds = new Dictionary<Guid, object>(4 + allExtensions.Count); + loadedDomainModelIds.Add(CoreDomainModel.DomainModelId, null); + loadedDomainModelIds.Add(Microsoft.VisualStudio.Modeling.Diagrams.CoreDesignSurfaceDomainModel.DomainModelId, null); + loadedDomainModelIds.Add(ObjectModel.ORMCoreDomainModel.DomainModelId, null); + loadedDomainModelIds.Add(ShapeModel.ORMShapeDomainModel.DomainModelId, null); + foreach (ORMExtensionType extensionType in extensions) + { + loadedDomainModelIds.Add(extensionType.Type.GUID, null); + } + + for (int i = 0; i < extensions.Count; i++) // The count check here is correct, we're growing the list as we go + { + foreach (ExtendsDomainModelAttribute extendsDomainModelAttribute in extensions[i].Type.GetCustomAttributes(typeof(ExtendsDomainModelAttribute), false)) + { + Guid extendedModelId = extendsDomainModelAttribute.ExtendedModelId; + if (!loadedDomainModelIds.ContainsKey(extendedModelId)) + { + // Find the missing domain model + foreach (ORMExtensionType candidateExtensionType in allExtensions) + { + object[] domainObjectIdAttributes = candidateExtensionType.Type.GetCustomAttributes(typeof(DomainObjectIdAttribute), false); + if (domainObjectIdAttributes.Length <= 0) + { + continue; + } + Guid candidateModelId = ((DomainObjectIdAttribute)domainObjectIdAttributes[0]).Id; + if (extendedModelId.Equals(candidateModelId)) + { + loadedDomainModelIds.Add(candidateModelId, null); + extensions.Add(candidateExtensionType); + break; + } + } + // If we didn't find the requested domain model, we don't need to worry about doing anything here, + // since the Store will throw later when they try to load the requesting domain model. + } + } + } + } + /// <summary> /// This is a custom callback class for the XSLT file that is /// responsible for adding or removing the custom extension namespaces to the ORM document. /// </summary> @@ -225,7 +230,7 @@ /// <param name="stream">The file stream that contains the ORM file.</param> /// <param name="extensionTypes">A list of extension types.</param> /// <returns>The cleaned stream.</returns> - private static Stream CleanupStream(Stream stream, IList<ORMExtensionType> extensionTypes) + public static Stream CleanupStream(Stream stream, IList<ORMExtensionType> extensionTypes) { MemoryStream outputStream = new MemoryStream((int)stream.Length); XsltArgumentList argList = new XsltArgumentList(); Modified: trunk/ORMModel/Shell/ORMDocData.cs =================================================================== --- trunk/ORMModel/Shell/ORMDocData.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/ORMModel/Shell/ORMDocData.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -35,6 +35,7 @@ using System.Xml.Schema; using System.Collections.ObjectModel; using Neumont.Tools.Modeling.Shell.DynamicSurveyTreeGrid; +using System.Runtime.InteropServices; namespace Neumont.Tools.ORM.Shell { @@ -690,6 +691,120 @@ #endregion // Tab Restoration Hack #region Automation support /// <summary> + /// Class used for the document extensibility layer. Request + /// the "ORMExtensionManager" extension object. + /// </summary> + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + public class ORMExtensionManagerAutomationObject + { + private ORMDesignerDocData myDocData; + /// <summary> + /// Create a new <see cref="ORMExtensionManagerAutomationObject"/> for the specific <paramref name="docData"/> + /// </summary> + /// <param name="docData">An <see cref="ORMDesignerDocData"/> instance</param> + public ORMExtensionManagerAutomationObject(ORMDesignerDocData docData) + { + myDocData = docData; + } + /// <summary> + /// Retrieve a string array of loaded extension names + /// </summary> + public string[] GetLoadedExtensions() + { + IList<ORMExtensionType> availableExtensions = ORMDesignerPackage.GetAvailableCustomExtensions(); + List<string> extensionNames = new List<string>(); + foreach (DomainModel domainModel in myDocData.Store.DomainModels) + { + Type domainModelType = domainModel.GetType(); + foreach (ORMExtensionType extensionInfo in availableExtensions) + { + if (extensionInfo.Type == domainModelType) + { + extensionNames.Add(extensionInfo.NamespaceUri); + break; + } + } + } + return extensionNames.ToArray(); + } + /// <summary> + /// Verify that the requested extensions are loaded in the current designer instance + /// </summary> + public void EnsureExtensions(string[] extensions) + { + int ensureCount; + if (extensions != null && (ensureCount = extensions.Length) != 0) + { + string[] clonedExtensions = (string[])extensions.Clone(); + IList<ORMExtensionType> availableExtensions = ORMDesignerPackage.GetAvailableCustomExtensions(); + List<ORMExtensionType> loadedExtensions = null; + foreach (DomainModel domainModel in myDocData.Store.DomainModels) + { + Type domainModelType = domainModel.GetType(); + foreach (ORMExtensionType extensionInfo in availableExtensions) + { + if (extensionInfo.Type == domainModelType) + { + string namespaceUri = extensionInfo.NamespaceUri; + for (int i = 0; i < clonedExtensions.Length; ++i) + { + if (clonedExtensions[i] == namespaceUri) + { + --ensureCount; + if (ensureCount == 0) + { + return; // Nothing to do, everything we need is already loaded + } + if (loadedExtensions == null) + { + loadedExtensions = new List<ORMExtensionType>(); + } + loadedExtensions.Add(extensionInfo); + clonedExtensions[i] = null; + } + } + break; + } + } + } + for (int i = 0; i < clonedExtensions.Length; ++i) + { + string newExtension = clonedExtensions[i]; + if (newExtension != null) + { + --ensureCount; + foreach (ORMExtensionType extensionInfo in availableExtensions) + { + if (extensionInfo.NamespaceUri == newExtension) + { + if (loadedExtensions == null) + { + loadedExtensions = new List<ORMExtensionType>(); + } + loadedExtensions.Add(extensionInfo); + break; + } + } + if (ensureCount == 0) + { + break; + } + } + } + Object streamObj; + (myDocData as EnvDTE.IExtensibleObject).GetAutomationObject("ORMXmlStream", null, out streamObj); + Stream stream = streamObj as Stream; + + Debug.Assert(stream != null); + + ExtensionManager.AddRequiredExtensions(loadedExtensions, availableExtensions); + stream = ExtensionManager.CleanupStream(stream, loadedExtensions); + myDocData.ReloadFromStream(stream); + } + } + } + /// <summary> /// Implements IExtensibleObject.GetAutomationObject. Returns the ORM XML stream for /// the "ORMXmlStream" object name and the this object for everything else. /// </summary> @@ -703,6 +818,14 @@ result = stream; return; } + else if ("ORMExtensionManager" == name) + { + // This returns an object with two methods: + // GetLoadedExtensions returns an array of current loaded extension objects + // EnsureExtensions accepts an array of extensions that need to be loaded + result = new ORMExtensionManagerAutomationObject(this); + return; + } result = this; } void IExtensibleObject.GetAutomationObject(string Name, IExtensibleObjectSite pParent, out object ppDisp) Modified: trunk/Tools/ORMCustomTool/Extender.cs =================================================================== --- trunk/Tools/ORMCustomTool/Extender.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/Tools/ORMCustomTool/Extender.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -17,6 +17,7 @@ using System.Runtime.InteropServices; using System.ComponentModel; using System.Drawing.Design; +using System.Diagnostics; namespace Neumont.Tools.ORM.ORMCustomTool { @@ -78,6 +79,15 @@ /// </summary> ~Extender() { + NotifyDeleteCookie(); + } + /// <summary> + /// Helper method to prevent the debugger from breaking when a common + /// exception is thrown deleting the cookie. + /// </summary> + [DebuggerStepThrough] + private void NotifyDeleteCookie() + { // Wrap this call in a try-catch to avoid any failure code the // Site may return. For instance, since this object is GC'ed, // the Site may have already let go of its Cookie. @@ -88,7 +98,7 @@ mySite.NotifyDelete(myCookie); } } - catch + catch { } } Modified: trunk/Tools/ORMCustomTool/IORMGenerator.cs =================================================================== --- trunk/Tools/ORMCustomTool/IORMGenerator.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/Tools/ORMCustomTool/IORMGenerator.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -93,6 +93,14 @@ } /// <summary> + /// Get required extensions for an associated input format. The extensions ensure that the input formats + /// meet additional content requirements not expressible based solely on file format. + /// </summary> + /// <param name="inputFormat">The official name of the required input format, retrieved from <see cref="RequiresInputFormats"/>.</param> + /// <returns>An enumerable of required extensions. The extensions must be enabled by the generator chosen to produce the given input format.</returns> + IEnumerable<string> GetRequiredExtensionsForInputFormat(string inputFormat); + + /// <summary> /// Returns the default name of the file generated for a specific source file name. /// </summary> /// <param name="sourceFileName">A <see cref="String"/> containing the name (without file extension) of the source ORM file.</param> Modified: trunk/Tools/ORMCustomTool/ORMCustomTool.cs =================================================================== --- trunk/Tools/ORMCustomTool/ORMCustomTool.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/Tools/ORMCustomTool/ORMCustomTool.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -517,9 +517,15 @@ ormStream = new MemoryStream(Encoding.UTF8.GetBytes(bstrInputFileContents), false); } + // Switch the ormStream to a readonly stream so we can reuse it multiple times + ormStream = new ReadOnlyStream(ormStream); + // Add the input ORM file Stream... - outputFormatStreams.Add(ORMOutputFormat.ORM, new ReadOnlyStream(ormStream)); + outputFormatStreams.Add(ORMOutputFormat.ORM, ormStream); + ORMExtensionManager ormExtensionManager = new ORMExtensionManager(projectItemDocument, ormStream); + string[] ormExtensions = null; // Delay populated if a requires is made + // Null out bstrInputFileContents to prevent its usage beyond this point. bstrInputFileContents = null; @@ -572,8 +578,30 @@ Stream readonlyOutputStream = null; try { - ormGenerator.GenerateOutput(buildItem, outputStream, readonlyOutputFormatStreams, wszDefaultNamespace); - readonlyOutputStream = new ReadOnlyStream(outputStream); + // UNDONE: Extension checking should happen in the current generator + // going back to the generator that produced the input file. We're only + // extending ORM files right now, and the ORM file doesn't have a generator, + // so we just do it here. + bool extensionsSatisfied = true; + foreach (string extension in ormGenerator.GetRequiredExtensionsForInputFormat("ORM")) + { + if (null == ormExtensions) + { + ormExtensions = ormExtensionManager.GetLoadedExtensions(); + } + if (Array.BinarySearch<string>(ormExtensions, extension) < 0) + { + extensionsSatisfied = false; + // UNDONE: Localize error messages. + message = string.Format(CultureInfo.InvariantCulture, "The extension '{0}' in the '{1}' is required for generation of the '{2}' file. The existing contents of '{3}' will not be modified. Open the 'ORM Generator Selection' dialog and choose 'Save Changes' to automatically add required extensions.", extension, "ORM", ormGenerator.OfficialName, buildItem.FinalItemSpec); + report(message, ReportType.Error, null); + } + } + if (extensionsSatisfied) + { + ormGenerator.GenerateOutput(buildItem, outputStream, readonlyOutputFormatStreams, wszDefaultNamespace); + readonlyOutputStream = new ReadOnlyStream(outputStream); + } } catch (Exception ex) { Modified: trunk/Tools/ORMCustomTool/ORMCustomTool.csproj =================================================================== --- trunk/Tools/ORMCustomTool/ORMCustomTool.csproj 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/Tools/ORMCustomTool/ORMCustomTool.csproj 2007-09-10 23:46:57 UTC (rev 1124) @@ -74,6 +74,7 @@ <Reference Include="VSLangProj80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> </ItemGroup> <ItemGroup> + <Compile Include="ORMExtensionManager.cs" /> <Compile Include="Extender.cs" /> <Compile Include="ExtenderProvider.cs" /> <Compile Include="UI\BranchBase.cs"> Added: trunk/Tools/ORMCustomTool/ORMExtensionManager.cs =================================================================== --- trunk/Tools/ORMCustomTool/ORMExtensionManager.cs (rev 0) +++ trunk/Tools/ORMCustomTool/ORMExtensionManager.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -0,0 +1,237 @@ +#region Common Public License Copyright Notice +/**************************************************************************\ +* Neumont Object-Role Modeling Architect for Visual Studio * +* * +* Copyright \xA9 Neumont University. All rights reserved. * +* * +* The use and distribution terms for this software are covered by the * +* Common Public License 1.0 (http://opensource.org/licenses/cpl) which * +* can be found in the file CPL.txt at the root of this distribution. * +* By using this software in any fashion, you are agreeing to be bound by * +* the terms of this license. * +* * +* You must not remove this notice, or any other, from this software. * +\**************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System.Reflection; +using System.Xml; +using System.Windows.Forms; + +namespace Neumont.Tools.ORM.ORMCustomTool +{ + /// <summary> + /// Extensions are additional named requirements for a given document format. + /// The extensions are treated as required content within a document format. + /// UNDONE: In the longer run, individual generators will be asked to ensure that + /// a given set of extensions are present in a returned document. For now, we + /// are not treating the document retrieved from the ORM designer as a true generator, + /// so we're just hacking this in. + /// </summary> + internal class ORMExtensionManager + { + private string[] myLoadedExtensions; + private EnvDTE.Document myDocument; + private EnvDTE.ProjectItem myProjectItem; + private Stream myStream; + public ORMExtensionManager(EnvDTE.Document document, Stream stream) + { + myDocument = document; + myStream = stream; + } + public ORMExtensionManager(EnvDTE.ProjectItem projectItem) + { + myProjectItem = projectItem; + } + /// <summary> + /// Return a sorted array of loaded extensions + /// </summary> + public string[] GetLoadedExtensions() + { + string[] retVal = myLoadedExtensions; + if (retVal == null) + { + if (myDocument == null && myProjectItem != null) + { + myDocument = myProjectItem.Document; + } + + object documentExtensionManager; + MethodInfo methodInfo; + if (null != myDocument && + null != (documentExtensionManager = myDocument.Object("ORMExtensionManager")) && + null != (methodInfo = documentExtensionManager.GetType().GetMethod("GetLoadedExtensions", Type.EmptyTypes))) + { + retVal = methodInfo.Invoke(documentExtensionManager, null) as string[]; + myDocument = null; // No longer needed + } + Stream stream = null; + if (null == retVal) + { + // First used the passed in stream + stream = myStream; + + // Next use an open text document. Note that this will already have provided + // an extension manager if this is an open ORM designer + if (stream == null && myDocument != null) + { + EnvDTE.TextDocument textDoc = myDocument.Object("TextDocument") as EnvDTE.TextDocument; + if (textDoc != null) + { + // Note that the stream will be closed with the default reader settings of the XmlReader below + stream = new MemoryStream(Encoding.UTF8.GetBytes(textDoc.StartPoint.CreateEditPoint().GetText(textDoc.EndPoint)), false); + } + } + + // Try the file directly from the project item + if (stream == null && myProjectItem != null) + { + // Note that the stream will be closed with the default reader settings of the XmlReader below + stream = new FileStream(myProjectItem.get_FileNames(0), FileMode.Open); + } + } + if (stream != null) + { + myStream = null; // No longer needed + // Attempt to open the stream as an Xml file to + // get the required extensions from the Xml + string[] namespaces = null; + int namespaceCount = 0; + try + { + XmlReaderSettings readerSettings = new XmlReaderSettings(); + readerSettings.CloseInput = true; + using (XmlReader reader = XmlReader.Create(stream, readerSettings)) + { + reader.MoveToContent(); + if (reader.NodeType == XmlNodeType.Element) + { + int attributeCount = reader.AttributeCount; + if (attributeCount != 0) + { + namespaces = new string[attributeCount]; + if (reader.MoveToFirstAttribute()) + { + do + { + if (reader.Prefix == "xmlns" || reader.Name == "xmlns") + { + // Note that some of these are standard, not extensions, but it + // isn't worth the trouble to add extra ORM knowledge here to figure it out + string value = reader.Value; + if (!string.IsNullOrEmpty(value)) + { + namespaces[namespaceCount] = reader.Value; + ++namespaceCount; + } + } + } while (reader.MoveToNextAttribute()); + } + } + } + } + } + catch (XmlException) + { + // Nothing to do + } + if (namespaceCount != 0) + { + if (namespaceCount != namespaces.Length) + { + Array.Resize(ref namespaces, namespaceCount); + } + retVal = namespaces; + } + } + if (retVal == null) + { + retVal = new string[0]; + } + else + { + Array.Sort<string>(retVal); + } + myLoadedExtensions = retVal; + } + return retVal; + } + /// <summary> + /// Ensure that the current project item has the required extensions loaded. + /// This is only called after verifying that the current project item does + /// not satisfy the requirements. + /// </summary> + /// <param name="projectItem">The <see cref="EnvDTE.ProjectItem"/> to modify</param> + /// <param name="extensions">An <see cref="T:ICollection{System.String}"/> of additional required extensions</param> + public static bool EnsureExtensions(EnvDTE.ProjectItem projectItem, ICollection<string> extensions) + { + ServiceProvider provider = new ServiceProvider((Microsoft.VisualStudio.OLE.Interop.IServiceProvider)projectItem.DTE); + // UNDONE: Localize message strings in here + if ((int)DialogResult.No == VsShellUtilities.ShowMessageBox( + provider, + "Additional extensions are required to support the chosen generators. Would you like to load the required extensions now?", + "ORM Generator Selection", + OLEMSGICON.OLEMSGICON_QUERY, + OLEMSGBUTTON.OLEMSGBUTTON_YESNO, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST)) + { + return false; + } + EnvDTE.Document document = projectItem.Document; + bool secondPass = false; + bool tryDocument = true; + while (tryDocument) + { + object documentExtensionManager; + MethodInfo methodInfo; + if (null != document && + null != (documentExtensionManager = document.Object("ORMExtensionManager")) && + null != (methodInfo = documentExtensionManager.GetType().GetMethod("EnsureExtensions", new Type[] { typeof(string[]) }))) + { + string[] extensionsArray = new string[extensions.Count]; + extensions.CopyTo(extensionsArray, 0); + methodInfo.Invoke(documentExtensionManager, new object[] { extensionsArray }); + return true; + } + + if (secondPass) + { + return false; + } + tryDocument = false; + secondPass = true; + + // UNDONE: Localize message strings in here + if ((int)DialogResult.No == VsShellUtilities.ShowMessageBox( + provider, + "The .ORM file must be open in the default designer to add extensions. Would you like to open or reopen the document now?", + "ORM Generator Selection", + OLEMSGICON.OLEMSGICON_QUERY, + OLEMSGBUTTON.OLEMSGBUTTON_YESNO, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST)) + { + return false; + } + + if (document != null) + { + document.Close(EnvDTE.vsSaveChanges.vsSaveChangesPrompt); + document = projectItem.Document; + } + if (document == null) + { + projectItem.Open(Guid.Empty.ToString("B")).Visible = true; + document = projectItem.Document; + tryDocument = true; + } + } + return false; + } + } +} Modified: trunk/Tools/ORMCustomTool/UI/MainBranch.cs =================================================================== --- trunk/Tools/ORMCustomTool/UI/MainBranch.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/Tools/ORMCustomTool/UI/MainBranch.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -69,6 +69,21 @@ } } + public IEnumerable<IORMGenerator> SelectedGenerators + { + get + { + foreach (OutputFormatBranch branch in _branches.Values) + { + IORMGenerator selectedGenerator = branch.SelectedORMGenerator; + if (selectedGenerator != null) + { + yield return selectedGenerator; + } + } + } + } + public bool IsPrimaryDisplayItem(int index) { OutputFormatBranch branch = _branches.Values[index]; Modified: trunk/Tools/ORMCustomTool/UI/ORMGeneratorSelectionControl.cs =================================================================== --- trunk/Tools/ORMCustomTool/UI/ORMGeneratorSelectionControl.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/Tools/ORMCustomTool/UI/ORMGeneratorSelectionControl.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -206,6 +206,42 @@ { if (_savedChanges) { + // Make sure the current document has the necessary + // extensions loaded. + // UNDONE: We should be able to do this with the document + // closed or open as text as well via a registered service + // on the ORMDesignerPackage, but this is sufficient for now. + Dictionary<string, string> requiredExtensions = null; + string[] loadedExtensions = null; + foreach (IORMGenerator selectedGenerator in _mainBranch.SelectedGenerators) + { + foreach (string requiredExtension in selectedGenerator.GetRequiredExtensionsForInputFormat("ORM")) + { + if (loadedExtensions == null) + { + loadedExtensions = (new ORMExtensionManager(_projectItem)).GetLoadedExtensions(); + } + if (Array.BinarySearch<string>(loadedExtensions, requiredExtension) < 0) + { + if (requiredExtensions == null) + { + requiredExtensions = new Dictionary<string, string>(); + } + else if (requiredExtensions.ContainsKey(requiredExtension)) + { + continue; + } + requiredExtensions.Add(requiredExtension, requiredExtension); + } + } + } + if (requiredExtensions != null) + { + _savedChanges = ORMExtensionManager.EnsureExtensions(_projectItem, requiredExtensions.Values); + } + } + if (_savedChanges) + { // Delete the removed items from the project if (_removedItems != null) { Modified: trunk/Tools/ORMCustomTool/XslORMGenerator.cs =================================================================== --- trunk/Tools/ORMCustomTool/XslORMGenerator.cs 2007-09-10 23:11:55 UTC (rev 1123) +++ trunk/Tools/ORMCustomTool/XslORMGenerator.cs 2007-09-10 23:46:57 UTC (rev 1124) @@ -64,17 +64,27 @@ this._generatesSupportFile = Convert.ToBoolean((int)generatorKey.GetValue("GeneratesSupportFile", 0)); this._customTool = generatorKey.GetValue("CustomTool", null) as string; - string sourceInputFormat = this._sourceInputFormat = generatorKey.GetValue("SourceInputFormat", null) as string; + Dictionary<string, IEnumerable<string>> extensions = null; + string sourceInputFormat = generatorKey.GetValue("SourceInputFormat", null) as string; Debug.Assert(sourceInputFormat != null); - string[] referenceInputFormats = this._referenceInputFormats = generatorKey.GetValue("ReferenceInputFormats", EmptyStringArray) as string[]; + string[] referenceInputFormats = generatorKey.GetValue("ReferenceInputFormats", EmptyStringArray) as string[]; string[] prequisiteInputFormats = generatorKey.GetValue("PrequisiteInputFormats", EmptyStringArray) as string[]; List<string> requiresInputFormats; requiresInputFormats = new List<string>(referenceInputFormats.Length + prequisiteInputFormats.Length + 1); - requiresInputFormats.Add(sourceInputFormat); - requiresInputFormats.AddRange(referenceInputFormats); - requiresInputFormats.AddRange(prequisiteInputFormats); + requiresInputFormats.Add(sourceInputFormat = StripFormatExtensions(sourceInputFormat, ref extensions)); + for (int i = 0; i < referenceInputFormats.Length; ++i) + { + requiresInputFormats.Add(referenceInputFormats[i] = StripFormatExtensions(referenceInputFormats[i], ref extensions)); + } + for (int i = 0; i < prequisiteInputFormats.Length; ++i) + { + requiresInputFormats.Add(StripFormatExtensions(prequisiteInputFormats[i], ref extensions)); + } + this._sourceInputFormat = sourceInputFormat; + this._referenceInputFormats = referenceInputFormats; + this._requiredExtensions = extensions; this._requiresInputFormats = new ReadOnlyCollection<string>(requiresInputFormats); this._transform = new XslCompiledTransform(System.Diagnostics.Debugger.IsAttached); @@ -87,6 +97,46 @@ } } + private static readonly char[] _extensionSplitChars = new char[] { ' ' }; + private static string StripFormatExtensions(string format, ref Dictionary<string, IEnumerable<string>> extensions) + { + string[] substrings = format.Split(_extensionSplitChars, StringSplitOptions.RemoveEmptyEntries); + int substringCount = substrings.Length; + if (substringCount > 0) + { + format = substrings[0]; + if (substringCount > 1) + { + string[] oldExtensions = null; + if (extensions == null) + { + extensions = new Dictionary<string, IEnumerable<string>>(); + oldExtensions = null; + } + else + { + IEnumerable<string> oldEntry; + if (extensions.TryGetValue(format, out oldEntry)) + { + oldExtensions = (string[])oldEntry; + } + } + int oldLength = (oldExtensions != null) ? oldExtensions.Length : 0; + string[] newExtensions = new string[oldLength + substringCount - 1]; + if (oldLength != 0) + { + oldExtensions.CopyTo(newExtensions, 0); + } + for (int i = 1; i < substringCount; ++i, ++oldLength) + { + newExtensions[oldLength] = substrings[i]; + } + extensions[format] = newExtensions; + } + } + return format; + } + private void LoadTransform() { this._transform.Load(this._transformCanonicalUri, XsltSettings.TrustedXslt, XmlResolver); @@ -148,6 +198,16 @@ get { return this._providesOutputFormat; } } + private readonly IDictionary<string, IEnumerable<string>> _requiredExtensions; + public IEnumerable<string> GetRequiredExtensionsForInputFormat(string outputFormat) + { + IDictionary<string, IEnumerable<string>> dictionary = _requiredExtensions; + IEnumerable<string> retVal; + return (dictionary != null && dictionary.TryGetValue(outputFormat, out retVal)) ? + retVal : + EmptyStringArray; + } + private readonly bool _generatesSupportFile; public bool GeneratesSupportFile { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |