/* * Copyright (c) 2003-2008 The University of Wroclaw. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the University may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System.Xml; using System.Xml.Serialization; using Nemerle.Compiler; using Nemerle.Compiler.Typedtree; using Nemerle.Collections; namespace Nemerle.Xml.Serialization { [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Field, Inherited = false, AllowMultiple = false)] macro XmlMultidimensionalArray (t : TypeBuilder, f : ParsedField) { f.modifiers.custom_attrs = <[ XmlIgnore ]> :: f.modifiers.custom_attrs; def (rank, elementType) = match (f.ty) { | <[ array [ $(rank : int), $t] ]> => (rank, t) | _ => Message.FatalError ("type of field `" + f.Name + "' is not multidimensional array") } def fname = Macros.UseSiteSymbol (f.Name); mutable bodySequence = []; /** we will create general body of getter similar to: def n = ordered.GetLength (0); def m = ordered.GetLength (1); def result = array (n); for (mutable i = 0; i < n; ++i) { result [i] = array (m); for (mutable j = 0; j < m; ++j) result[i][j] = ordered [i, j]; } result */ /// first create variables containing lengths of dimensions */ def lengths = array (rank); for (mutable i = 0; i < rank; ++i) { lengths [i] = Macros.NewSymbol (); bodySequence = <[ def $(lengths [i] : name) = $(fname : name).GetLength ($(i : int)) ]> :: bodySequence; } def lengthsIndex = List.Map (List.FromArray (lengths), fun (x) { <[ $(x : name) ]> }); //// create variable containing top array with result bodySequence = <[ def result = array ($(lengths [0] : name)) ]> :: bodySequence; /// recursively define [for] loops iterating through nested arrays def nest (depth, assign, indices) { if (depth >= rank) { <[ $assign = $(fname : name) [ ..$(List.Rev (indices)) ] ]> } else { def i = Macros.NewSymbol (); def newAssign = <[ $assign [$(i : name)] ]>; def newIndices = <[ $(i : name) ]> :: indices; <[ $assign = array ($(lengths [depth - 1] : name)); for (mutable $(i : name) = 0; $(i : name) < $(lengths [depth] : name); ++ $(i : name)) $(nest (depth + 1, newAssign, newIndices)) ]> } } /// top loop must be explicit, because we don't have [assign] expression here def firstAssign = <[ result [firstIdx] ]>; def firstIndices = [ <[ firstIdx ]> ]; bodySequence = <[ for (mutable firstIdx = 0; firstIdx < $(lengths [0] : name); ++firstIdx) $(nest (1, firstAssign, firstIndices)) ]> :: bodySequence; /// finally create returning of result bodySequence = <[ result ]> :: bodySequence; def getter = <[ { .. $(List.Rev (bodySequence)) } ]>; /** we will create general body of setter similar to: for (mutable i = 0; i < value.Length; ++i) for (mutable j = 0; j < value[i].Length; ++j) ordered [i, j] = value[i][j] */ /// we will use previously defined [lengths] variable as indices /// recursively define [for] loops iterating through nested arrays def nest (depth, val) { if (depth >= rank) <[ $(fname : name) [..$lengthsIndex ] = $val ]> else { def newVal = <[ $val [$(lengths[depth] : name)] ]>; <[ for (mutable $(lengths[depth] : name) = 0; $(lengths[depth] : name) < $val . Length; ++ $(lengths[depth] : name)) $(nest (depth + 1, newVal)) ]> } } def setter = nest (0, <[ $(Macros.UseSiteSymbol ("value") : name) ]>); /// build property used to serialize and deserialize our field def build_type (depth) { | 0 => elementType | _ => <[ array [ 1, $(build_type (depth - 1))] ]> } t.Define (<[ decl: [XmlRootAttribute($(f.Name : string))] public $(Macros.NewSymbol () : name) : $(build_type (rank)) { get { $getter } set { $setter } } ]>) } /** Allow XML serialization of a variant. It adds a default public ctor to variant options, prevents _N_constant_object optimization and add appropriate XmlInclude attributes. */ [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeTypedMembers, Nemerle.MacroTargets.Class, Inherited = false, AllowMultiple = false)] macro XmlSerializable (par : TypeBuilder) { par.Define (<[ decl: private _N_dummy : int = 0; ]>); match (par.GetTydecl ()) { | TypeDeclaration.Variant (lst) => def mods = par.GetModifiers (); foreach (ti :> TypeBuilder in lst) { ti.Define (<[ decl: public this () { } ]>); // prevent _N_constant_object def expr = <[ XmlInclude (typeof ($(ti.ParsedName : name))) ]>; mods.AddCustomAttribute (expr); } | _ => Message.Error ("the `XmlSerializable' macro can be used only on variants") } } }