/*
Uiml.Net: a Uiml.Net renderer (http://lumumba.uhasselt.be/kris/research/uiml.net/)
Copyright (C) 2003 Kris Luyten (kris.luyten@uhasselt.be)
Expertise Centre for Digital Media (http://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.
*/
/*
TODO:
- add support for property placeholders in scripts. E.g. like asp.net allows
C#/vb.net code inside html
- add support for result capturing, so results from scripts can be used inside the user interface
*/
namespace Uiml.Executing
{
using Uiml;
using Uiml.Utils.Reflection;
using System;
using System.Xml;
using System.Collections;
using System.IO;
using System.Reflection;
#if !COMPACT
using System.CodeDom.Compiler;
#endif
public class Script : IExecutable, IUimlElement
{
private String m_scriptSource;
private String m_type;
private bool m_preCompiled = false;
private Assembly m_compiledAssembly;
private Object m_retValue = null;
public Script()
{
}
public Script(XmlNode xmlNode, Part partTop) : this()
{
Process(xmlNode);
}
public Script(String scriptType, String scriptSource) :this()
{
Source = scriptSource;
Type = scriptType;
}
public Script(XmlNode n) : this()
{
//TODO this should be replaced by
//"waiting for the parent Part" when the Script
//element is more complete
Process(n);
}
public void Process(XmlNode n)
{
if(n.Name != IAM)
return;
XmlAttributeCollection attr = n.Attributes;
if(attr.GetNamedItem(TYPE) != null)
Type = attr.GetNamedItem(TYPE).Value;
else
Console.WriteLine("Warning: " + IAM + " should have \"" + TYPE + "\" attribute!");
Source = n.InnerText;
}
public void GetEvents(ArrayList al)
{
}
///
///Loads a CodeProvider for the language used for inline scripting
///
///
///Load CodeProviders dynamically...
///
protected void PreCompile()
{
#if !COMPACT
CodeDomProvider theProvider = null;
switch(Type)
{
case "CSharp":
case "C#":
case "csharp":
case "C Sharp":
case "C sharp":
theProvider = new Microsoft.CSharp.CSharpCodeProvider();
break;
case "JScript":
// FIXME
Console.WriteLine("JScript does not yet work with Mono.");
return;
//theProvider = new Microsoft.JScript.JScriptCodeProvider();
//break;
case "Visual Basic":
case "VB":
case "VB.Net":
case "vb":
case "vb.net":
case "VB.NET":
theProvider = new Microsoft.VisualBasic.VBCodeProvider();
break;
case "Nemerle":
case "nemerle":
theProvider = DynamicallyLoadLanguage("Nemerle", NEMERLE_LIBS, NEMERLE_CODE_PROVIDER, NEMERLE_CODE_PROVIDER_LIB_INDEX);
if (theProvider == null)
return; // an error has occured, just quit ...
break;
case "Boo":
case "boo":
theProvider = DynamicallyLoadLanguage("Boo", BOO_LIBS, BOO_CODE_PROVIDER, BOO_CODE_PROVIDER_LIB_INDEX);
if (theProvider == null)
return; // an error has occured, just quit ...
break;
default:
theProvider = new Microsoft.CSharp.CSharpCodeProvider();
break;
}
ICodeCompiler theCompiler = theProvider.CreateCompiler();
CompilerParameters compParams = new CompilerParameters();
IEnumerator enumLibs = ExternalLibraries.Instance.LoadedAssemblies;
while(enumLibs.MoveNext())
{
Console.WriteLine("[Inline Script precompile linking]: " + enumLibs.Current);
compParams.ReferencedAssemblies.Add(((Assembly)enumLibs.Current).Location);
}
compParams.GenerateExecutable = true;
compParams.GenerateInMemory = true;
CompilerResults crs = theCompiler.CompileAssemblyFromSource(compParams,Source);
if(crs.Errors.HasErrors)
for(int i=0; i< crs.Errors.Count; i++)
Console.WriteLine("[Inline script precompile error]:" + crs.Errors[i]);
m_compiledAssembly = crs.CompiledAssembly;
#endif
}
#if !COMPACT
private CodeDomProvider DynamicallyLoadLanguage(string lang, string[] libNames, string codeProvider, int codeProviderLibIndex)
{
ArrayList assemblies = new ArrayList();
try
{
// dynamically load language libraries
foreach (string libName in libNames)
{
Console.Write("[Loading {0} library]: {1} ... ", lang, libName);
Assembly lib = AssemblyLoader.LoadAny(libName);
if (lib != null)
{
assemblies.Add(lib);
Console.WriteLine("OK!");
Console.WriteLine("\t--> {0}", lib.FullName);
}
else
{
Console.WriteLine("Failed :-(");
throw new FileNotFoundException("Couldn't load assembly {0}", libName);
}
}
Assembly codeProviderlib = (Assembly) assemblies[codeProviderLibIndex];
Type t = codeProviderlib.GetType(codeProvider);
return (CodeDomProvider) Activator.CreateInstance(t);
}
catch (Exception err)
{
Console.WriteLine("Error while loading Boo libraries: {0}", err);
return null;
}
}
#endif
public Object Execute(String name, Object[] parameters)
{
return Execute();
}
public Object Execute(Object[] parameters)
{
return Execute();
}
public Object Execute()
{
if(!m_preCompiled)
PreCompile();
// FIXME: remove this test when JScript works
if (m_compiledAssembly == null)
return null;
// let's execute m_compiledAssembly here
Type[] types = m_compiledAssembly.GetTypes();
// Boo and Nemerle have non-public Main methods
BindingFlags flags = ( BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic );
foreach(Type t in types)
{
MethodInfo[] mi = t.GetMethods(flags);
foreach(MethodInfo m in mi)
{
if(m.Name == "Main")
{
try
{
m_retValue = m.Invoke(null, null);
}
catch (TargetParameterCountException)
{
// parameter to main method is string[] instead of void
m_retValue = m.Invoke(null, new object[] { new string[] {} });
}
}
}
}
return null;
}
public Object Execute(Uiml.Rendering.IRenderer renderer)
{
return Execute();
}
public ArrayList Children
{
get { return null; }
}
public String Source
{
get { return m_scriptSource; }
set { m_scriptSource = value; }
}
public String Type
{
get { return m_type; }
set { m_type = value; }
}
public Object ReturnValue
{
get { return m_retValue; }
}
public const string IAM = "script";
public const string TYPE = "type";
public const string NEMERLE_CODE_PROVIDER = "Nemerle.Compiler.NemerleCodeProvider";
public const int NEMERLE_CODE_PROVIDER_LIB_INDEX = 1; // the second lib
public static string[] NEMERLE_LIBS = {
"Nemerle",
"Nemerle.Compiler",
"Nemerle.Macros",
};
public const string BOO_CODE_PROVIDER = "Boo.Lang.CodeDom.BooCodeProvider";
public const int BOO_CODE_PROVIDER_LIB_INDEX = 1; // the second lib
public static string[] BOO_LIBS = {
"Boo.Lang",
"Boo.Lang.CodeDom",
"Boo.Lang.Compiler",
"Boo.Lang.Interpreter",
"Boo.Lang.Parser",
"Boo.Lang.Useful"
};
}
}