/main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs
C# | 373 lines | 279 code | 61 blank | 33 comment | 34 complexity | 78069013fe1ac5ed274e65a71dceba81 MD5 | raw file
1//
2// TemplatingHost.cs
3//
4// Author:
5// Michael Hutchinson <mhutchinson@novell.com>
6//
7// Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26
27using System;
28using System.Collections.Generic;
29using System.CodeDom.Compiler;
30using System.IO;
31using System.Text;
32using Microsoft.VisualStudio.TextTemplating;
33
34namespace Mono.TextTemplating
35{
36 public class TemplateGenerator : MarshalByRefObject, ITextTemplatingEngineHost
37 {
38 //re-usable
39 TemplatingEngine engine;
40
41 //per-run variables
42 string inputFile, outputFile;
43 Encoding encoding;
44
45 //host fields
46 CompilerErrorCollection errors = new CompilerErrorCollection ();
47 List<string> refs = new List<string> ();
48 List<string> imports = new List<string> ();
49 List<string> includePaths = new List<string> ();
50 List<string> referencePaths = new List<string> ();
51
52 //host properties for consumers to access
53 public CompilerErrorCollection Errors { get { return errors; } }
54 public List<string> Refs { get { return refs; } }
55 public List<string> Imports { get { return imports; } }
56 public List<string> IncludePaths { get { return includePaths; } }
57 public List<string> ReferencePaths { get { return referencePaths; } }
58 public string OutputFile { get { return outputFile; } }
59
60 public TemplateGenerator ()
61 {
62 Refs.Add (typeof (TextTransformation).Assembly.Location);
63 Refs.Add (typeof(System.Uri).Assembly.Location);
64 Imports.Add ("System");
65 }
66
67 public CompiledTemplate CompileTemplate (string content)
68 {
69 if (String.IsNullOrEmpty (content))
70 throw new ArgumentNullException ("content");
71
72 errors.Clear ();
73 encoding = Encoding.UTF8;
74
75 return Engine.CompileTemplate (content, this);
76 }
77
78 protected TemplatingEngine Engine {
79 get {
80 if (engine == null)
81 engine = new TemplatingEngine ();
82 return engine;
83 }
84 }
85
86 public bool ProcessTemplate (string inputFile, string outputFile)
87 {
88 if (String.IsNullOrEmpty (inputFile))
89 throw new ArgumentNullException ("inputFile");
90 if (String.IsNullOrEmpty (outputFile))
91 throw new ArgumentNullException ("outputFile");
92
93 string content;
94 try {
95 content = File.ReadAllText (inputFile);
96 } catch (IOException ex) {
97 errors.Clear ();
98 AddError ("Could not read input file '" + inputFile + "':\n" + ex.ToString ());
99 return false;
100 }
101
102 string output;
103 ProcessTemplate (inputFile, content, ref outputFile, out output);
104
105 try {
106 if (!errors.HasErrors)
107 File.WriteAllText (outputFile, output, encoding);
108 } catch (IOException ex) {
109 AddError ("Could not write output file '" + outputFile + "':\n" + ex.ToString ());
110 }
111
112 return !errors.HasErrors;
113 }
114
115 public bool ProcessTemplate (string inputFileName, string inputContent, ref string outputFileName, out string outputContent)
116 {
117 errors.Clear ();
118 encoding = Encoding.UTF8;
119
120 this.outputFile = outputFileName;
121 this.inputFile = inputFileName;
122 outputContent = Engine.ProcessTemplate (inputContent, this);
123 outputFileName = this.outputFile;
124
125 return !errors.HasErrors;
126 }
127
128 public bool PreprocessTemplate (string inputFile, string className, string classNamespace,
129 string outputFile, System.Text.Encoding encoding, out string language, out string[] references)
130 {
131 language = null;
132 references = null;
133
134 if (string.IsNullOrEmpty (inputFile))
135 throw new ArgumentNullException ("inputFile");
136 if (string.IsNullOrEmpty (outputFile))
137 throw new ArgumentNullException ("outputFile");
138
139 string content;
140 try {
141 content = File.ReadAllText (inputFile);
142 } catch (IOException ex) {
143 errors.Clear ();
144 AddError ("Could not read input file '" + inputFile + "':\n" + ex.ToString ());
145 return false;
146 }
147
148 string output;
149 PreprocessTemplate (inputFile, className, classNamespace, content, out language, out references, out output);
150
151 try {
152 if (!errors.HasErrors)
153 File.WriteAllText (outputFile, output, encoding);
154 } catch (IOException ex) {
155 AddError ("Could not write output file '" + outputFile + "':\n" + ex.ToString ());
156 }
157
158 return !errors.HasErrors;
159 }
160
161 public bool PreprocessTemplate (string inputFileName, string className, string classNamespace, string inputContent,
162 out string language, out string[] references, out string outputContent)
163 {
164 errors.Clear ();
165 encoding = Encoding.UTF8;
166
167 this.inputFile = inputFileName;
168 outputContent = Engine.PreprocessTemplate (inputContent, this, className, classNamespace, out language, out references);
169
170 return !errors.HasErrors;
171 }
172
173 CompilerError AddError (string error)
174 {
175 CompilerError err = new CompilerError ();
176 err.ErrorText = error;
177 Errors.Add (err);
178 return err;
179 }
180
181 #region Virtual members
182
183 public virtual object GetHostOption (string optionName)
184 {
185 return null;
186 }
187
188 public virtual AppDomain ProvideTemplatingAppDomain (string content)
189 {
190 return null;
191 }
192
193 //FIXME: implement
194 protected virtual string ResolveAssemblyReference (string assemblyReference)
195 {
196 //foreach (string referencePath in ReferencePaths) {
197 //
198 //}
199 return assemblyReference;
200 }
201
202 protected virtual string ResolveParameterValue (string directiveId, string processorName, string parameterName)
203 {
204 var key = new ParameterKey (processorName, directiveId, parameterName);
205 string value;
206 if (parameters.TryGetValue (key, out value))
207 return value;
208 if (processorName != null || directiveId != null)
209 return ResolveParameterValue (null, null, parameterName);
210 return null;
211 }
212
213 protected virtual Type ResolveDirectiveProcessor (string processorName)
214 {
215 KeyValuePair<string,string> value;
216 if (!directiveProcessors.TryGetValue (processorName, out value))
217 throw new Exception (string.Format ("No directive processor registered as '{0}'", processorName));
218 var asmPath = ResolveAssemblyReference (value.Value);
219 if (asmPath == null)
220 throw new Exception (string.Format ("Could not resolve assembly '{0}' for directive processor '{1}'", value.Value, processorName));
221 var asm = System.Reflection.Assembly.LoadFrom (asmPath);
222 return asm.GetType (value.Key, true);
223 }
224
225 protected virtual string ResolvePath (string path)
226 {
227 path = System.Environment.ExpandEnvironmentVariables (path);
228 if (Path.IsPathRooted (path))
229 return path;
230 var dir = Path.GetDirectoryName (inputFile);
231 var test = Path.Combine (dir, path);
232 if (File.Exists (test))
233 return test;
234 return null;
235 }
236
237 #endregion
238
239 Dictionary<ParameterKey,string> parameters = new Dictionary<ParameterKey, string> ();
240 Dictionary<string,KeyValuePair<string,string>> directiveProcessors = new Dictionary<string, KeyValuePair<string,string>> ();
241
242 public void AddDirectiveProcessor (string name, string klass, string assembly)
243 {
244 directiveProcessors.Add (name, new KeyValuePair<string,string> (klass,assembly));
245 }
246
247 public void AddParameter (string processorName, string directiveName, string parameterName, string value)
248 {
249 parameters.Add (new ParameterKey (processorName, directiveName, parameterName), value);
250 }
251
252 protected virtual bool LoadIncludeText (string requestFileName, out string content, out string location)
253 {
254 content = "";
255 location = ResolvePath (requestFileName);
256
257 if (location == null) {
258 foreach (string path in includePaths) {
259 string f = Path.Combine (path, requestFileName);
260 if (File.Exists (f)) {
261 location = f;
262 break;
263 }
264 }
265 }
266
267 if (location == null)
268 return false;
269
270 try {
271 content = File.ReadAllText (location);
272 return true;
273 } catch (IOException ex) {
274 AddError ("Could not read included file '" + location + "':\n" + ex.ToString ());
275 }
276 return false;
277 }
278
279 #region Explicit ITextTemplatingEngineHost implementation
280
281 bool ITextTemplatingEngineHost.LoadIncludeText (string requestFileName, out string content, out string location)
282 {
283 return LoadIncludeText (requestFileName, out content, out location);
284 }
285
286 void ITextTemplatingEngineHost.LogErrors (CompilerErrorCollection errors)
287 {
288 this.errors.AddRange (errors);
289 }
290
291 string ITextTemplatingEngineHost.ResolveAssemblyReference (string assemblyReference)
292 {
293 return ResolveAssemblyReference (assemblyReference);
294 }
295
296 string ITextTemplatingEngineHost.ResolveParameterValue (string directiveId, string processorName, string parameterName)
297 {
298 return ResolveParameterValue (directiveId, processorName, parameterName);
299 }
300
301 Type ITextTemplatingEngineHost.ResolveDirectiveProcessor (string processorName)
302 {
303 return ResolveDirectiveProcessor (processorName);
304 }
305
306 string ITextTemplatingEngineHost.ResolvePath (string path)
307 {
308 return ResolvePath (path);
309 }
310
311 void ITextTemplatingEngineHost.SetFileExtension (string extension)
312 {
313 extension = extension.TrimStart ('.');
314 if (Path.HasExtension (outputFile)) {
315 outputFile = Path.ChangeExtension (outputFile, extension);
316 } else {
317 outputFile = outputFile + "." + extension;
318 }
319 }
320
321 void ITextTemplatingEngineHost.SetOutputEncoding (System.Text.Encoding encoding, bool fromOutputDirective)
322 {
323 this.encoding = encoding;
324 }
325
326 IList<string> ITextTemplatingEngineHost.StandardAssemblyReferences {
327 get { return refs; }
328 }
329
330 IList<string> ITextTemplatingEngineHost.StandardImports {
331 get { return imports; }
332 }
333
334 string ITextTemplatingEngineHost.TemplateFile {
335 get { return inputFile; }
336 }
337
338 #endregion
339
340 struct ParameterKey : IEquatable<ParameterKey>
341 {
342 public ParameterKey (string processorName, string directiveName, string parameterName)
343 {
344 this.processorName = processorName ?? "";
345 this.directiveName = directiveName ?? "";
346 this.parameterName = parameterName ?? "";
347 unchecked {
348 hashCode = this.processorName.GetHashCode ()
349 ^ this.directiveName.GetHashCode ()
350 ^ this.parameterName.GetHashCode ();
351 }
352 }
353
354 string processorName, directiveName, parameterName;
355 int hashCode;
356
357 public override bool Equals (object obj)
358 {
359 return obj != null && obj is ParameterKey && Equals ((ParameterKey)obj);
360 }
361
362 public bool Equals (ParameterKey other)
363 {
364 return processorName == other.processorName && directiveName == other.directiveName && parameterName == other.parameterName;
365 }
366
367 public override int GetHashCode ()
368 {
369 return hashCode;
370 }
371 }
372 }
373}