1 /*
2  * hunt-proton: AMQP Protocol library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.proton.codec.AbstractDescribedType;
13 
14 import hunt.proton.codec.DecoderImpl;
15 import hunt.proton.codec.EncoderImpl;
16 import hunt.proton.codec.TypeEncoding;
17 import hunt.proton.codec.AMQPType;
18 import hunt.proton.codec.EncodingCodes;
19 import hunt.proton.amqp.UnsignedLong;
20 
21 import hunt.collection.Map;
22 import hunt.collection.HashMap;
23 import hunt.collection.Collection;
24 import hunt.collection.ArrayList;
25 import hunt.Exceptions;
26 import hunt.logging.ConsoleLogger;
27 
28 abstract class AbstractDescribedType(T,M) : AMQPType!(T)  //!(AmqpValue,Object)
29 {
30     private DecoderImpl _decoder;
31     private EncoderImpl _encoder;
32     private Map!(TypeEncoding!(M), TypeEncoding!(T)) _encodings ;
33 
34     this(EncoderImpl encoder)
35     {
36         _encoder = encoder;
37         _decoder = encoder.getDecoder();
38         _encodings = new HashMap!(TypeEncoding!(M), TypeEncoding!(T));
39     }
40 
41     abstract protected UnsignedLong getDescriptor();
42 
43     public EncoderImpl getEncoder()
44     {
45         return _encoder;
46     }
47 
48     public DecoderImpl getDecoder()
49     {
50         return _decoder;
51     }
52 
53     public ITypeEncoding getEncoding(Object v)
54     {
55         version(HUNT_AMQP_DEBUG) {
56             if(v is null) {
57                 warning("v is null");
58             } else {
59                 infof("wrapping %s", typeid(v));
60             }
61         }
62 
63         T val = cast(T)v;
64 
65         if(val is null) {
66             warningf("Wrong casting from '%s' to '%s'", typeid(v), T.stringof);
67         }
68         //M asUnderlying = wrap(val);
69         //TypeEncoding<M> underlyingEncoding = _encoder.getType(asUnderlying).getEncoding(asUnderlying);
70         //TypeEncoding<T> encoding = _encodings.get(underlyingEncoding);
71         //if(encoding == null)
72         //{
73         //    encoding = new DynamicDescribedTypeEncoding(underlyingEncoding);
74         //    _encodings.put(underlyingEncoding, encoding);
75         //}
76 
77         //protected List!Object wrap(Header val)
78         //{
79         //    return new HeaderWrapper(val);
80         //}
81         //String
82         M asUnderlying = wrap(val); //ArrayList!Object
83 
84         if(asUnderlying is null) {
85             warningf("asUnderlying is null for %s", typeid(val));
86         } else {
87             version(HUNT_AMQP_DEBUG) trace(typeid(cast(Object)asUnderlying));
88         }
89 
90         IAMQPType tt = _encoder.getType(cast(Object)asUnderlying);
91         version(HUNT_AMQP_DEBUG) trace(typeid(cast(Object)tt));
92 
93        // IAMQPType tt = _encoder.getType(cast(Object)asUnderlying,typeid(M));
94        ITypeEncoding typeEncoding = tt.getEncoding(cast(Object)asUnderlying);
95         TypeEncoding!(M) underlyingEncoding = cast(TypeEncoding!(M))typeEncoding;
96 
97         if(underlyingEncoding is null) {
98             warningf("Wrong casting from '%s' to '%s'", typeid(typeEncoding), typeid(TypeEncoding!(M)));
99             return null;
100         }
101 
102        // TypeEncoding!(M) underlyingEncoding = (cast(AMQPType!M)(_encoder.getType(cast(Object)asUnderlying,this.getTypeClass()))).getEncoding(asUnderlying);
103         TypeEncoding!(T) encoding = _encodings.get(underlyingEncoding);
104         if(encoding is null)
105         {
106             encoding = new DynamicDescribedTypeEncoding(underlyingEncoding);
107             _encodings.put(underlyingEncoding, encoding);
108         }
109         return cast(ITypeEncoding)encoding;
110         //implementationMissing(false);
111         //return null;
112     }
113 
114     abstract protected M wrap(T val);
115 
116     public TypeEncoding!(T) getCanonicalEncoding()
117     {
118         return null;
119     }
120 
121     public  Collection!(TypeEncoding!(T)) getAllEncodings()
122     {
123        // auto lst = new ArrayList!(TypeEncoding!(T));
124 
125         return new ArrayList!(TypeEncoding!(T)) (_encodings.values());
126        // Collection unmodifiable = Collections.unmodifiableCollection(values);
127        // return (Collection!(TypeEncoding!(T))) unmodifiable;
128     }
129 
130     public void write(Object val)
131     {
132         //T t = cast(T)val;
133         //assert(t !is null);
134         ITypeEncoding encoding = getEncoding(val);
135         if(encoding is null) {
136             warning("encoding is null");
137         } else {
138             encoding.writeConstructor();
139             encoding.writeValue(val);
140         }
141     }
142 
143     class DynamicDescribedTypeEncoding : TypeEncoding!(T)
144     {
145         private TypeEncoding!(M) _underlyingEncoding;
146         private TypeEncoding!(UnsignedLong) _descriptorType;
147         private int _constructorSize;
148 
149 
150         this(TypeEncoding!(M) underlyingEncoding)
151         {
152             _underlyingEncoding = underlyingEncoding;
153             _descriptorType = cast(TypeEncoding!(UnsignedLong))(_encoder.getType(getDescriptor()).getEncoding(getDescriptor()));
154             _constructorSize = 1 + _descriptorType.getConstructorSize()
155                                + _descriptorType.getValueSize(getDescriptor())
156                                + _underlyingEncoding.getConstructorSize();
157         }
158 
159         public AMQPType!(T) getType()
160         {
161            // return AbstractDescribedType.this;
162             return this.outer;
163         }
164 
165 
166         int opCmp(ITypeEncoding o)
167         {
168             return this.getConstructorSize - o.getConstructorSize;
169         }
170 
171        // alias opCmp = Object.opCmp;
172 
173         public void writeConstructor()
174         {
175             _encoder.writeRaw(EncodingCodes.DESCRIBED_TYPE_INDICATOR);
176             _descriptorType.writeConstructor();
177             _descriptorType.writeValue(getDescriptor());
178             _underlyingEncoding.writeConstructor();
179         }
180 
181         public int getConstructorSize()
182         {
183             return _constructorSize;
184         }
185 
186         public void writeValue(Object val)
187         {
188             _underlyingEncoding.writeValue(cast(Object)wrap(cast(T)val));
189         }
190 
191         public int getValueSize(Object val)
192         {
193             return _underlyingEncoding.getValueSize(cast(Object)wrap(cast(T)val));
194         }
195 
196         public bool isFixedSizeVal()
197         {
198             return _underlyingEncoding.isFixedSizeVal();
199         }
200 
201         public bool encodesSuperset(TypeEncoding!(T) encoding)
202         {
203             return (getType() == encoding.getType())
204                    && (_underlyingEncoding.encodesSuperset((cast(DynamicDescribedTypeEncoding)encoding)
205                                                                    ._underlyingEncoding));
206         }
207 
208         override
209         public bool encodesJavaPrimitive()
210         {
211             return false;
212         }
213 
214     }
215 }