1 /**
2 *   Copyright: © 2012-2014 Anton Gushcha
3 *   License: Subject to the terms of the MIT license, as written in the included LICENSE file.
4 *   Authors:  NCrashed <ncrashed@gmail.com>,
5 *             LeMarwin <lemarwin42@gmail.com>,
6 *             Nazgull09 <nazgull90@gmail.com>
7 */
8 module devol.type;
9 
10 import std.array;
11 import devol.serializable;
12 import devol.typemng;
13 
14 import dyaml.all;    
15 
16 public
17 {
18 	import devol.argument;
19 }
20 
21 alias Argument delegate(Argument val) ConvertorFunc;
22  
23 class ConvAddException : Exception
24 {
25 	this(string s, ConvertorFunc conv, Type type)
26 	{
27 		super(s);
28 		eConv = conv;
29 		eType = type;
30 	}
31 	
32 	ConvertorFunc eConv;
33 	Type eType;
34 }
35 
36 class ConvException : Exception
37 {
38 	this( string s, Type from, Type to)
39 	{
40 		super(s);
41 		eFrom = from;
42 		eTo = to;
43 	}
44 
45 	Type eFrom;
46 	Type eTo;
47 }
48 
49 abstract class Type : ISerializable
50 {
51 	this(string name)
52 	{
53 		sName = name;
54 	}
55 	
56 	@property string name()
57 	{
58 		return sName;
59 	}
60 	
61 	Argument getNewArg()
62 	{
63 		throw new Exception("Pure type isn't allowed to generate args!");
64 	}
65 	
66 	Argument getNewArg(string min, string max, string[] exVal)
67 	{
68 		throw new Exception("Pure type isn't allowed to generate args!");
69 	}
70 	
71 	void registerConvertor( ConvertorFunc func, Type toType )
72 	{
73 		if (toType is null) return;
74 		
75 		if (toType in convs)
76 		{
77 			throw new ConvAddException("Convertor already exists!", convs[toType], toType);
78 		}
79 		
80 		convs[toType] = func;
81 	}
82 	
83 	Argument convert(Argument val)
84 	{
85 		Type type = val.type;
86 		
87 		if(type.name == name ) return val;
88 		
89 		if (this in type.convs)
90 		{
91 			return type.convs[this](val);
92 		}
93 		
94 		auto chain = findConvWay(type);
95 		if (chain.empty)
96 		{
97 			throw new ConvException("Types uncovertable!", type, this);
98 		}
99 		
100 		foreach(conv; chain)
101 		{
102 			val = conv(val);
103 		}
104 		return val;
105 	}
106 	
107 	bool isConvertable(Type from)
108 	{
109 		if (from.name == name ) return true;
110 		
111 		if (this in from.convs)
112 		{
113 			return true;
114 		}
115 		
116 		auto chain = findConvWay(from);		
117 		return !chain.empty;
118 	}
119 	
120 	private ConvertorFunc[] findConvWay(Type from)
121 	{
122 		class Node
123 		{
124 			Node parent;
125 			Type type;
126 			ConvertorFunc func;
127 		}
128 		
129 		Node[] stack = new Node[0];
130 		Type[] blackList = new Type[0];
131 		
132 		Node prevNode = new Node;
133 		prevNode.parent = null;
134 		prevNode.func = null;
135 		bool finded = false;
136 		
137 		finddo: do
138 		{
139 			blackList ~= from;
140 			foreach(type,conv; from.convs)
141 			{
142 				Node node = new Node;
143 				node.parent = prevNode;
144 				node.func = conv;
145 				
146 				bool isInBL(Type t, Type[] bl)
147 				{
148 					foreach( type; bl)
149 						if (t == type)
150 							return true;
151 					return false;
152 				}
153 				
154 				if (!isInBL(type, blackList))
155 					stack ~= node;
156 				
157 				if (type == this)
158 				{
159 					finded = true;
160 					break finddo;
161 				}
162 			}
163 			
164 			if (!stack.empty)
165 			{
166 				prevNode = stack[0];
167 				from = prevNode.type;
168 				stack = stack[1..$];
169 			}
170 			
171 		} while(!stack.empty);
172 		
173 		auto chain = new ConvertorFunc[0];
174 		if (!finded)
175 			return chain;
176 			
177 		auto node = stack[$-1];
178 		while(node.parent !is null)
179 		{
180 			chain = node.func ~ chain;
181 			node = node.parent;
182 		}
183 		return chain;
184 	}
185 	
186 	static Type loadBinary(InputStream stream)
187 	{
188 	    char[] typename;
189 	    stream.read(typename);
190 	    
191 	    return TypeMng.getSingleton().getType(typename.idup);
192 	}
193 	
194 	static Type loadYaml(Node node)
195 	{
196 	    return TypeMng.getSingleton().getType(node.as!string);
197 	}
198 	
199 	/// Loading argument from input stream
200 	/**
201 	*  Should be defined by all childs.
202 	*/
203 	Argument loadArgument(InputStream stream);
204 	
205 	/// Loading argument from input stream
206     /**
207     *  Should be defined by all childs.
208     */
209 	Argument loadArgument(Node node);
210 	
211 	void saveBinary(OutputStream stream)
212 	{
213         stream.write(sName);
214 	}
215 	
216 	Node saveYaml()
217 	{
218 	    return Node(sName);
219 	}
220 	
221 	private ConvertorFunc[Type] convs;
222 	private string sName;
223 }