using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; using System.Xml.Linq; using System.Xml.Schema; using Web.Core; using Patcher.Data.Command; using Patcher.DB; namespace Patcher.Data.Patch { abstract class AbstractPatch { private static class Checker { private static XmlSchemaSet LoadXsd() { XmlSchemaSet schemaSet = new XmlSchemaSet(); using(Stream xsdStream = Resources.ResourcesManager.GetResource("IPatch.xsd")) { using(XmlReader xsdReader = XmlReader.Create(xsdStream)) { schemaSet.Add("", xsdReader); } } schemaSet.Compile(); return schemaSet; } private static readonly XmlSchemaSet Xsd = LoadXsd(); public static void Check(XDocument document) { document.Validate(Xsd, (sender, e) => { throw new ApplicationException("Intermediate XML validation failed: " + e.Message, e.Exception); }); } } public static AbstractPatch LoadById(PatchId id, Context context) { return Cache.instance.get(new KeyValuePair(id, context), () => _LoadById(id, context)); } private static AbstractPatch _LoadById(PatchId id, Context context) { XDocument data; using(Stream xmlStream = context.loadPatch(id)) { using(XmlReader reader = XmlReader.Create(xmlStream)) { data = XDocument.Load(reader); } } Checker.Check(data); XElement version = data.Root.Element("version"); string number = version.Element("number").Value; string author = version.Element("author").Value; if((number != id.version.ToString()) || (author != id.name)) { throw new ApplicationException(string.Format("Versions mismatch on patch #{0} from {1} (got #{2} from {3})", id.version, id.name, number, author)); } HashSet restrictToEnvironments; if(data.Root.Element("restrictToEnvironments") != null) { restrictToEnvironments = new HashSet(from elem in data.Root.Element("restrictToEnvironments").Elements() select elem.Value); } else { restrictToEnvironments = new HashSet(); } XElement commandSet; bool isStrictCommandSet; { XElement strictCommandSet = data.Root.Element("strictCommandSet"); XElement looseCommandSet = data.Root.Element("looseCommandSet"); if (strictCommandSet != null && looseCommandSet == null) { commandSet = strictCommandSet; isStrictCommandSet = true; } else if (strictCommandSet == null && looseCommandSet != null) { commandSet = looseCommandSet; isStrictCommandSet = false; } else if (strictCommandSet != null && looseCommandSet != null) { throw new ApplicationException("Malformed XML: both strictCommandSet and looseCommandSet found"); } else { throw new ApplicationException("Malformed XML: no CommandSet found"); } } var commands = (from elem in commandSet.Elements() select AbstractCommand.Create(elem)).ToArray(); if(isStrictCommandSet) { var isPersistent = commands.OfType().Any(); if (isPersistent) { if (commands.Length != 1) { throw new ApplicationException("More than one persistent command"); } if(context.DbDriver.IsDDLTransactional) { return new AtomicPatch(commands, true, restrictToEnvironments, context); } else { return new PersistentPatch(commands[0], restrictToEnvironments, context); } } else { return new AtomicPatch(commands, true, restrictToEnvironments, context); } } else { return new AtomicPatch(commands, false, restrictToEnvironments, context); } } private readonly HashSet restrictToEnvironments; protected readonly Context context; protected AbstractPatch(HashSet restrictToEnvironments, Context context) { this.restrictToEnvironments = restrictToEnvironments; this.context = context; } public abstract XDocument Apply(Transaction transaction); public abstract void Rollback(Transaction transaction, XDocument rollbackInfo); public bool DoesSupportEnvironment(string environmentName) { if(restrictToEnvironments.Any()) { return restrictToEnvironments.Contains(environmentName); } else { return true; } } } }