GME  13
ReferenceSwitcher.cs
Go to the documentation of this file.
00001 using System;
00002 using System.Collections.Generic;
00003 using System.IO;
00004 using System.Runtime.InteropServices;
00005 using System.Text;
00006 using GME.MGA.Core;
00007 using GME.CSharp;
00008 using GME;
00009 using GME.MGA;
00010 using System.Linq;
00011 
00012 namespace CSGUI
00013 {
00017     [Guid("0ACC000C-29E6-418B-9F8B-968418C533B9"),
00018     ProgId("CSGUI.ReferenceSwitcher"),
00019     ClassInterface(ClassInterfaceType.AutoDual)]
00020     [ComVisible(true)]
00021     public class ReferenceSwitcher
00022     {
00023         MgaGateway MgaGateway { get; set; }
00024         GMEConsole GMEConsole { get; set; }
00025 
00026         public void SwitchReferences(object[] lib1Objects, object[] lib2Objects)
00027         {
00028             Switcher switcher = new Switcher(lib1Objects.Cast<IMgaObject>(), lib2Objects.Cast<IMgaObject>(), null);
00029             switcher.UpdateSublibrary();
00030         }
00031 
00032         [ComVisible(false)]
00033         public void PrintLine(Func<string, string, string> f, IMgaFCO a, IMgaFCO b)
00034         {
00035             Console.Out.WriteLine(f(Switcher.getPath(a), Switcher.getPath(b)));
00036         }
00037         public void SwitchReference(IMgaFCO to, IMgaReference @ref)
00038         {
00039             Switcher.MoveReferenceWithRefportConnections(to, @ref, PrintLine);
00040         }
00041     }
00042 
00043     [ComVisible(false)]
00044     public class Switcher
00045     {
00046         List<IMgaObject> lib1Objects;
00047         List<IMgaObject> lib2Objects;
00048         GMEConsole GMEConsole;
00049 
00050         public Switcher(IMgaObject fco1Object, IMgaObject fco2Object, GMEConsole GMEConsole)
00051         {
00052             this.lib1Objects = new List<IMgaObject>() { fco1Object };
00053             this.lib2Objects = new List<IMgaObject>() { fco2Object };
00054             this.GMEConsole = GMEConsole;
00055         }
00056         public Switcher(IEnumerable<IMgaObject> fco1Object, IEnumerable<IMgaObject> fco2Object, GMEConsole GMEConsole)
00057         {
00058             this.lib1Objects = fco1Object.ToList();
00059             this.lib2Objects = fco2Object.ToList();
00060             this.GMEConsole = GMEConsole;
00061         }
00062 
00063         class ObjectPair
00064         {
00065             public IMgaObject o1 { get; set; }
00066             public IMgaObject o2 { get; set; }
00067         }
00068 
00069         public void UpdateSublibrary()
00070         {
00071             int origPrefs = this.lib1Objects.First().Project.Preferences;
00072             // Magic word allows us to remove ConnPoints
00073             this.lib1Objects.First().Project.Preferences = origPrefs | (int)GME.MGA.preference_flags.MGAPREF_IGNORECONNCHECKS
00074                 | (int)GME.MGA.preference_flags.MGAPREF_FREEINSTANCEREFS;
00075             try
00076             {
00077                 IEnumerator<IMgaObject> lib2ObjectsEnum = lib2Objects.GetEnumerator();
00078                 foreach (IMgaObject lib1Object in lib1Objects)
00079                 {
00080                     lib2ObjectsEnum.MoveNext();
00081                     UpdateSublibrary(lib1Object, lib2ObjectsEnum.Current);
00082                 }
00083             }
00084             finally
00085             {
00086                 lib1Objects.First().Project.Preferences = origPrefs;
00087             }
00088         }
00089 
00090         private void UpdateSublibrary(IMgaObject fco1Objec, IMgaObject fco2Objec)
00091         {
00092             // fco2Object may be null
00093             if (fco1Objec is IMgaFCO) // references only refer to FCOs
00094             {
00095                 IMgaFCO fco1 = (IMgaFCO)fco1Objec;
00096                 IMgaFCO fco2 = fco2Objec as IMgaFCO;
00097                 foreach (IMgaFCO fco in fco1.ReferencedBy)
00098                 {
00099                     if (fco.IsInstance)
00100                         continue; // instance references will be updated by their archetype
00101                     // Don't update references in the old library
00102                     bool fcoInLib1Objects = false;
00103                     foreach (IMgaObject lib1Root in this.lib1Objects)
00104                     {
00105                         // FIXME: Contains(this.fco1Object) doesn't work
00106                         if (new ParentChain(fco).Contains(lib1Root, new MgaObjectEqualityComparor<IMgaObject>()))
00107                         {
00108                             fcoInLib1Objects = true;
00109                         }
00110                     }
00111                     if (fcoInLib1Objects)
00112                         continue;
00113                     IMgaReference refe = (IMgaReference)fco;
00114                     if (fco2 != null)
00115                     {
00116                         if (refe.UsedByConns.Count != 0)
00117                         {
00118                             if (refe.DerivedFrom == null)
00119                             {
00120                                 try
00121                                 {
00122                                     MoveReferenceWithRefportConnections(fco2, refe, WriteLine);
00123                                 }
00124                                 catch (Exception e)
00125                                 {
00126                                     if (GMEConsole != null)
00127                                         GMEConsole.Error.WriteLine("Could not set reference " + GetLink(refe, refe.Name));
00128                                     throw new Exception("Could not set reference " + getPath(refe) +
00129                                         " (" + refe.ID + ")", e);
00130                                 }
00131                             }
00132                         }
00133                         else
00134                         {
00135                             try
00136                             {
00137                                 bool setRef;
00138                                 if (refe.DerivedFrom == null)
00139                                     setRef = true;
00140                                 else
00141                                 {
00142                                     short compareToBase;
00143                                     refe.CompareToBase(out compareToBase);
00144                                     setRef = compareToBase != 0;
00145                                 }
00146                                 if (setRef)
00147                                 {
00148                                     // FIXME: can this fail; should we handle it somehow?
00149                                     refe.Referred = (GME.MGA.MgaFCO)fco2;
00150                                 }
00151                             }
00152                             catch (Exception e)
00153                             {
00154                                 if (GMEConsole != null)
00155                                     GMEConsole.Error.WriteLine("Could not set reference " + GetLink(refe, refe.Name));
00156                                 throw new Exception("Could not set reference " + getPath(refe) +
00157                                     " (" + refe.ID + ")", e);
00158                             }
00159                         }
00160                     }
00161                     else
00162                     {
00163                         WriteLine((x, y) => "Couldn't update " + x + ": " + y + " has no counterpart", refe, fco1);
00164                     }
00165                 }
00166             }
00167 
00168             List<IMgaObject> fco1Children = getChildren(fco1Objec);
00169             List<IMgaObject> fco2Children = getChildren(fco2Objec);
00170             Dictionary<string, ObjectPair> dict = new Dictionary<string, ObjectPair>();
00171             foreach (IMgaObject o in fco1Children)
00172             {
00173                 dict.GetValueOrDefault(o.Name + "xxx ;xxx" + o.MetaBase.Name).o1 = o;
00174             }
00175             foreach (IMgaObject o in fco2Children)
00176             {
00177                 dict.GetValueOrDefault(o.Name + "xxx ;xxx" + o.MetaBase.Name).o2 = o;
00178             }
00179             foreach (KeyValuePair<string, ObjectPair> entry in dict)
00180             {
00181                 if (entry.Value.o1 != null)
00182                 {
00183                     UpdateSublibrary(entry.Value.o1, entry.Value.o2);
00184                 }
00185             }
00186         }
00187 
00188 
00189         public delegate void WriteLineF(Func<string, string, string> f, IMgaFCO a, IMgaFCO b);
00190         public void WriteLine(Func<string, string, string> f, IMgaFCO a, IMgaFCO b)
00191         {
00192             if (GMEConsole != null)
00193             {
00194                 GMEConsole.Out.WriteLine(f(GetLink(a, a.Name), GetLink(b, b.Name)));
00195             }
00196             else
00197             {
00198                 Console.Out.WriteLine(f(getPath(a), getPath(b)));
00199             }
00200         }
00201 
00202         public static List<T> MakeList<T>(T itemOftype)
00203         {
00204             List<T> newList = new List<T>();
00205             return newList;
00206         }
00207 
00211         public static void MoveReferenceWithRefportConnections(IMgaFCO fco2, IMgaReference origref,
00212             WriteLineF WriteLine)
00213         {
00214             Queue<IMgaReference> references = new Queue<IMgaReference>();
00215             references.Enqueue(origref);
00216             IMgaFCO targetModel = fco2;
00217             while (targetModel is IMgaReference)
00218             {
00219                 targetModel = ((IMgaReference)targetModel).Referred;
00220             }
00221             MgaFCOs fco2ChildFCOs = ((IMgaModel)targetModel).ChildFCOs;
00222             Dictionary<string, IMgaFCO> newRefeChildren = GetNameMap(fco2ChildFCOs,
00223                 x => { });
00224             // TODO: warn, but only for refport-connected children
00225             //GMEConsole.Warning.WriteLine("Warning: " + fco2.Name + " has multiple children named " + x));
00226 
00227             int origPrefs = fco2.Project.Preferences;
00228             // Magic word allows us to remove ConnPoints
00229             fco2.Project.Preferences = origPrefs | (int)GME.MGA.preference_flags.MGAPREF_IGNORECONNCHECKS
00230                 | (int)GME.MGA.preference_flags.MGAPREF_FREEINSTANCEREFS;
00231 
00232             try {
00233 
00234                 MgaConnection conn = null;
00235                 var ReconnectList = MakeList( new { ConnRole = "src", Ref = origref, Port = fco2, Conn = conn } );
00236                 while( references.Count != 0 ) {
00237                     IMgaReference refe = references.Dequeue();
00238 
00239                     foreach (IMgaConnPoint connPoint in refe.UsedByConns) {
00240                         if (connPoint.References[1] != refe)
00241                         {
00242                             continue;
00243                         }
00244                         IMgaFCO fco2Port;
00245                         if( newRefeChildren.TryGetValue( connPoint.Target.Name, out fco2Port ) ) {
00246                             if( fco2Port == null ) {
00247                                 // fco2Port == null => multiple children with the same name
00248                                 // Try matching based on Kind too
00249                                 fco2Port = fco2ChildFCOs.Cast<IMgaFCO>().Where( x => x.Name == connPoint.Target.Name
00250                                     && x.Meta.MetaRef == connPoint.Target.Meta.MetaRef ).FirstOrDefault();
00251                             }
00252                             if( fco2Port != null ) {
00253                                 ReconnectList.Add( new { ConnRole = connPoint.ConnRole, Ref = refe, Port = fco2Port, Conn = connPoint.Owner } );
00254                                 connPoint.Remove();
00255                             }
00256                         } else {
00257                             WriteLine( ( x, y ) => "Can't find corresponding port for " + x
00258                                 + " in " + y, connPoint.Target, fco2 );
00259                             connPoint.Owner.DestroyObject();
00260                         }
00261                     }
00262                     foreach( IMgaReference x in refe.ReferencedBy.Cast<IMgaReference>() ) {
00263                         if( x.ID == origref.ID )
00264                             throw new Exception( "Circular reference chain starting with " + origref.AbsPath );
00265                         references.Enqueue( x );
00266                     }
00267                 }
00268                 origref.Referred = (MgaFCO)fco2;
00269                 foreach( var reconnect in ReconnectList ) {
00270                     if( reconnect.ConnRole == "src" )
00271                         ( reconnect.Conn as IMgaSimpleConnection ).SetSrc( (MgaFCOs)GetRefChain( reconnect.Ref ), (MgaFCO)reconnect.Port );
00272                     else
00273                         ( reconnect.Conn as IMgaSimpleConnection ).SetDst( (MgaFCOs)GetRefChain( reconnect.Ref ), (MgaFCO)reconnect.Port );
00274                 }
00275 
00276             } finally {
00277                 fco2.Project.Preferences = origPrefs;
00278             }
00279 
00280         }
00281 
00282         public static string GetLink(IMgaObject o, string linkText = null)
00283         {
00284             if (linkText == null)
00285             {
00286                 linkText = getPath(o);
00287             }
00288             return "<a href=\"mga:" + o.ID + "\">"
00289                 + linkText
00290                 + "</a>";
00291         }
00292 
00293         private static Dictionary<string, IMgaFCO> GetNameMap(IMgaFCOs fcos,
00294             Action<string> warnFunc)
00295         {
00296             Dictionary<string, IMgaFCO> refeChildren;
00297             refeChildren = new Dictionary<string, IMgaFCO>();
00298             foreach (IMgaFCO refeChild in fcos.Cast<IMgaFCO>())
00299             {
00300                 if (refeChild.ObjType == GME.MGA.Meta.objtype_enum.OBJTYPE_CONNECTION)
00301                     continue;
00302                 if (refeChildren.ContainsKey(refeChild.Name))
00303                 {
00304                     warnFunc(refeChild.Name);
00305                     refeChildren[refeChild.Name] = null;
00306                 }
00307                 else
00308                     refeChildren.Add(refeChild.Name, refeChild);
00309             }
00310             return refeChildren;
00311         }
00312 
00313         private static IMgaFCOs GetRefChain(IMgaReference reference)
00314         {
00315             IMgaFCOs ret = (IMgaFCOs)Activator.CreateInstance(Type.GetTypeFromProgID("Mga.MgaFCOs"));
00316             ret.Append(reference as MgaFCO);
00317             while (true)
00318             {
00319                 if (reference.Referred == null)
00320                 {
00321                     break;
00322                 }
00323                 if (reference.Referred.ObjType == GME.MGA.Meta.objtype_enum.OBJTYPE_REFERENCE)
00324                 {
00325                     reference = reference.Referred as IMgaReference;
00326                     ret.Append(reference as MgaFCO);
00327                 }
00328                 else
00329                 {
00330                     break;
00331                 }
00332             }
00333             return ret;
00334         }
00335 
00336         public static string getPath(IMgaObject fco1Object)
00337         {
00338             return String.Join("/", new ParentChain(fco1Object).Select(x => x.Name).Reverse().ToArray());
00339         }
00340 
00341         private List<IMgaObject> getChildren(IMgaObject fco1Objec)
00342         {
00343             List<IMgaObject> fco1Children = new List<IMgaObject>();
00344             if (fco1Objec == null)
00345             {
00346                 return fco1Children;
00347             }
00348             if (fco1Objec is IMgaFolder)
00349             {
00350                 fco1Children.AddRange((fco1Objec as IMgaFolder).ChildFolders.Cast<IMgaObject>());
00351                 fco1Children.AddRange((fco1Objec as IMgaFolder).ChildFCOs.Cast<IMgaObject>());
00352             }
00353             if (fco1Objec is IMgaModel)
00354             {
00355                 fco1Children.AddRange((fco1Objec as IMgaModel).ChildFCOs.Cast<IMgaObject>());
00356             }
00357             return fco1Children;
00358         }
00359     }
00360 
00361     [ComVisible(false)]
00362     class MgaObjectEqualityComparor<T> : EqualityComparer<T> where T : IMgaObject
00363     {
00364         public override bool Equals(T x, T y)
00365         {
00366             return x.ID.Equals(y.ID);
00367         }
00368 
00369         public override int GetHashCode(T obj)
00370         {
00371             return obj.ID.GetHashCode();
00372         }
00373     }
00374 
00375     [ComVisible(false)]
00376     static class DictionaryExtension
00377     {
00378         public static V GetValueOrDefault<K, V>(this Dictionary<K, V> dic, K key)
00379             where V : new()
00380         {
00381             V ret;
00382             bool found = dic.TryGetValue(key, out ret);
00383             if (found)
00384             {
00385                 return ret;
00386             }
00387             else
00388             {
00389                 ret = new V();
00390                 dic[key] = ret;
00391                 return ret;
00392             }
00393         }
00394     }
00395 }