/* Uiml.Net: a .Net UIML renderer (http://research.edm.uhasselt.be/kris/research/uiml.net) Copyright (C) 2005 Kris Luyten (kris.luyten@uhasselt.be) Expertise Centre for Digital Media (http://www.edm.uhasselt.be) Hasselt University This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ namespace Uiml.Rendering { using Uiml; using Uiml.Peers; using System; using System.Collections; using System.Reflection; /// /// This class implementens widget set independent behavior for the /// widget set specific backends. It implements the general core (widget creation, // applying proerties on widgets) of the rendering backends. /// public abstract class Renderer : IRenderer { private ITypeDecoder m_typeDecoder; private Assembly m_guiAssembly; private Vocabulary m_voc; private Part m_top; protected bool m_stopOnError = true; abstract public IRenderedInstance PreRender(UimlDocument uimlDoc); virtual public IRenderedInstance Render(UimlDocument uimlDoc) { return PreRender(uimlDoc); } abstract public IPropertySetter PropertySetter { get; } abstract protected System.Object LoadAdHocProperties(ref System.Object uiObject, Part part, Style s); /// /// the mapping voabulary that is used to convert the uiml document into the target widget set. /// public Vocabulary Voc { get { return m_voc; } set { m_voc = value; } } /// /// Provides the Decoder for the specific backend that is being used. The decoder converts /// (typeless) data values out of the UIML document to the types required by the backend. /// public ITypeDecoder Decoder { get { return m_typeDecoder; } set { m_typeDecoder = value; } } /// /// The assmebly that containts the widgets used by the backend renderer. /// /// /// This needs to be changed to support a widget sets whose widgets are spread /// over several assemblies (.dll files) /// public Assembly GuiAssembly { get { return m_guiAssembly; } set { m_guiAssembly = value; } } /// /// The top part of the structure out of the uiml document that is being rendered. /// /// /// The Top part is not always the root of the structure tree of the interface. /// A subtree of the structure can be rendered by the backend, by just passing the /// part top node of the subtree. /// public Part Top { get { return m_top; } set { m_top = value; } } /// /// Applies several properties to an individual concrete widget instance. /// It implements two stages: /// - In the first stage the widget properties are applied completely dynamic: /// relying on the reflection mechanisms the Style properties are queried and loaded /// - When the first stage fails, another method is called using ad hoc knowledge /// about the widget and its properties /// protected System.Object ApplyProperties(System.Object uiObject, Part part, Style style) { //should be provided by another "component" instead of by a function: //this way the structure can be reused more easily try { LoadAdHocProperties(ref uiObject, part, style); LoadPartProperties(ref uiObject, part); LoadNamedProperties(ref uiObject, part, style); LoadClassProperties(ref uiObject, part, style); } catch(MappingNotFoundException mnfe) { Console.WriteLine("Could not find appropriate mapping: " + mnfe.ToString()); if(m_stopOnError) #if COMPACT //TODO ; #else Environment.Exit(0); #endif } return uiObject; } private System.Object LoadPartProperties(ref System.Object uiObject, Part part) { string className = Voc.MapsOnCls(part.Class); Type classType = GuiAssembly.GetType(className); IEnumerator enumProps = part.Properties; while(enumProps.MoveNext()) { Property p = (Property)enumProps.Current; uiObject = ApplyProperty(ref uiObject, p, part, classType); } return uiObject; } /// ///Loads the named properties available in style for the gtkObject widget // private System.Object LoadNamedProperties(ref System.Object uiObject, Part part, Style style) { string className = Voc.MapsOnCls(part.Class); Type classType = GuiAssembly.GetType(className); IEnumerator enumProps = style.GetNamedProperties(part.Identifier); while(enumProps.MoveNext()) { Property p = (Property)enumProps.Current; uiObject = ApplyProperty(ref uiObject, p, part, classType); } return uiObject; } /// ///Loads the class properties available in style for the gtkObject widget // private System.Object LoadClassProperties(ref System.Object uiObject, Part part, Style style) { string className = Voc.MapsOnCls(part.Class); Type classType = GuiAssembly.GetType(className); IEnumerator enumProps = style.GetClassProperties(part.Identifier); while(enumProps.MoveNext()) { Property p = (Property)enumProps.Current; uiObject = ApplyProperty(ref uiObject, p, part, classType); } return uiObject; } /// ///This is the implementation of the method specified in the IPropertySetter Interface ///For now it is implemented in the rendering engine itself, but when it becomes ///too complex the IPropertySetter implementation will be isolated from ///this rendering class. /// ///The part on which prop will be applied ///the property that will be applied /// /// If part is null, the top part will be assumed /// public void ApplyProperty(Part part, Property prop) { if(part == null) part = m_top; Part p = part.SearchPart(prop.PartName); if(prop.Lazy) prop.Resolve(this); string className = Voc.MapsOnCls(p.Class); Type classType = GuiAssembly.GetType(className); System.Object uiObj = p.UiObject; ApplyProperty(ref uiObj, prop, p, classType); } /// /// This method sets the concrete property on a concrete widget. It is the most important /// method to get the defined properties reflected in the User Interface /// ///The (widget set dependent) object that represents the part in the widget set /// /// /// private System.Object ApplyProperty(ref System.Object uiObject, Property p, Part part, Type tclassType) { if (p.IsVirtual) { // don't do anything, just return the widget return uiObject; } string setter = Voc.GetPropertySetter(p.Name, part.Class); //try to use Properties first, //if that fails, look for the appropriate setters in the available methods try { Type classType = tclassType; System.Object targetObject = uiObject; PropertyInfo pInfo = null; int j = setter.IndexOf('.'); while(j!=-1) { String parentType = setter.Substring(0,j); setter=setter.Substring(j+1,setter.Length-j-1); pInfo = classType.GetProperty(parentType); classType = pInfo.PropertyType; targetObject = pInfo.GetValue(targetObject, null); j = setter.IndexOf('.'); } #if COMPACT MemberInfo[] arrayMemberInfo = SearchMembers(classType, setter); #else ///thanks to Rafael "Monoman" Teixeira for this code //pInfo = classType.GetProperty(setter); MemberInfo[] arrayMemberInfo = classType.FindMembers(MemberTypes.Method | MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, Type.FilterName, setter); #endif if (arrayMemberInfo == null || arrayMemberInfo.Length == 0) { // throw some error here, about not having the appropriate member Console.WriteLine("Warning: could not load setter \"{0}\" for {1} (type {2}), please check your vocabulary", setter, part.Identifier, tclassType.FullName); return uiObject; } //if lazy, resolve property value first if(p.Lazy) p.Resolve(this); // if (arrayMemberInfo[0] is PropertyInfo) SetProperty(targetObject, p, (PropertyInfo)arrayMemberInfo[0]); else InvokeMethod(targetObject, part, p, (MethodInfo)arrayMemberInfo[0]); } /* catch(TypeLoadException tle) { return ApplyAdHocProperty(ref uiObject, part, p); } */ catch(NullReferenceException nre) { Console.WriteLine("Warning: could not load setter \"{0}\" for {1} (type {2}), please check your vocabulary", setter, part.Identifier, tclassType.FullName); } catch(Exception e) { Console.WriteLine("Setting property [{0}] with value [{1}] failed for [{2}]", setter, p.Value, part.Identifier); Console.WriteLine(e); Console.WriteLine("Trying to continue..."); } return uiObject; } /// ///Sets the value of property p for the object targetobject /// private void SetProperty(System.Object targetObject, Property prop, PropertyInfo pInfo) { System.Object bla= Decoder.GetArg(prop.Value, pInfo.PropertyType); pInfo.SetValue(targetObject, bla, null); } /// /// Invokes the method "setter" that sets the value of the property prop for the object targetObject /// /// ///Add an ad-hoc procedure for matching interfaces, cfr. the ArgumentOutOfRangeException /// private void InvokeMethod(System.Object targetObject, Part part, Property prop, MethodInfo mInfo) { Param[] paramTypes = Voc.GetParams(prop.Name, part.Class); //convert the params to types Type[] tparamTypes = new Type[paramTypes.Length]; try { for(int i=0; i /// Dissects the method information for a specific property /// /// /// /// /// protected MemberInfo ResolveProperty(Type baseT, String newT, out Type retType, ref System.Object nextValue) { MemberInfo m = baseT.GetProperty(newT); if(m == null) { m = baseT.GetMethod(newT, new Type[0]); retType = ((MethodInfo)m).ReturnType; nextValue = ((MethodInfo)m).Invoke(nextValue, null); } else { retType = ((PropertyInfo)m).PropertyType; nextValue = ((PropertyInfo)m).GetValue(nextValue, null); } return m; } #if COMPACT protected MemberInfo[] SearchMembers(Type t, string setter) { ArrayList l = new ArrayList(); foreach(MemberInfo m in t.GetMembers()) { bool condition = (m.MemberType == MemberTypes.Property || m.MemberType == MemberTypes.Method) && m.Name == setter; if(condition) l.Add(m); } return (MemberInfo[])l.ToArray(typeof(MemberInfo)); } #endif } }