using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace arookas { public abstract class sunImportResolver { static sunImportResolver defaultResolver = new sunDefaultImportResolver(); public static sunImportResolver Default { get { return defaultResolver; } } public abstract void EnterFile(sunScriptFile file); public abstract void ExitFile(sunScriptFile file); public abstract sunImportResult ResolveImport(string name, out sunScriptFile file); // default implementation sealed class sunDefaultImportResolver : sunImportResolver { List imports = new List(10); Stack current = new Stack(5); string rootDirectory, currentDirectory; string CurrentDirectory { get { return current.Count > 0 ? Path.GetDirectoryName(current.Peek().Name) : currentDirectory; } } public sunDefaultImportResolver() { rootDirectory = AppDomain.CurrentDomain.BaseDirectory; currentDirectory = Directory.GetCurrentDirectory(); } public override void EnterFile(sunScriptFile file) { current.Push(file); } public override void ExitFile(sunScriptFile file) { current.Pop(); } public override sunImportResult ResolveImport(string name, out sunScriptFile file) { file = null; string fullPath; if (Path.IsPathRooted(name)) { // if the path is absolute, just use it directly fullPath = name; if (!File.Exists(fullPath)) { return sunImportResult.Missing; } } else { // check if the file exists relative to the current one; // if it's not there, check the root directory fullPath = Path.Combine(CurrentDirectory, name); if (!File.Exists(fullPath)) { fullPath = Path.Combine(rootDirectory, name); if (!File.Exists(fullPath)) { return sunImportResult.Missing; } } } // make sure the file has not been imported yet if (imports.Any(i => i.Name == fullPath)) { return sunImportResult.Skipped; } // open the file try { file = new sunScriptFile(name, File.OpenRead(fullPath)); } catch { return sunImportResult.FailedToLoad; } imports.Add(file); return sunImportResult.Loaded; } } } public enum sunImportResult { Loaded, Skipped, Missing, FailedToLoad, } public class sunScriptFile : IDisposable { public string Name { get; private set; } public Stream Stream { get; private set; } public sunScriptFile(string name, Stream stream) { if (name == null) { throw new ArgumentNullException("name"); } if (stream == null) { throw new ArgumentNullException("stream"); } if (!stream.CanRead) { throw new ArgumentException("Stream does not support reading.", "stream"); } Name = name; Stream = stream; } public void Dispose() { Stream.Dispose(); } public TextReader GetReader() { return new StreamReader(Stream); } } }