001/*
002 * BridJ - Dynamic and blazing-fast native interop for Java.
003 * http://bridj.googlecode.com/
004 *
005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/)
006 * All rights reserved.
007 *
008 * Redistribution and use in source and binary forms, with or without
009 * modification, are permitted provided that the following conditions are met:
010 * 
011 *     * Redistributions of source code must retain the above copyright
012 *       notice, this list of conditions and the following disclaimer.
013 *     * Redistributions in binary form must reproduce the above copyright
014 *       notice, this list of conditions and the following disclaimer in the
015 *       documentation and/or other materials provided with the distribution.
016 *     * Neither the name of Olivier Chafik nor the
017 *       names of its contributors may be used to endorse or promote products
018 *       derived from this software without specific prior written permission.
019 * 
020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030 */
031package org.bridj.cpp;
032
033import java.util.Stack;
034import java.util.Arrays;
035import java.util.List;
036import java.util.ArrayList;
037import java.lang.reflect.Type;
038import java.lang.reflect.ParameterizedType;
039
040import org.bridj.NativeObject;
041import org.bridj.Pointer;
042import org.bridj.StructObject;
043import org.bridj.ann.Runtime;
044import org.bridj.ann.Template;
045import org.bridj.cpp.mfc.MFCRuntime;
046import org.bridj.BridJ;
047
048/**
049 * Representation of a C++ type (including template parameters, which can be
050 * types or constants).
051 *
052 * @author Olivier
053 */
054public class CPPType implements ParameterizedType {
055
056    private final Type[] actualTypeArguments;
057    private final Type ownerType;
058    private final Type rawType;
059    private final Object[] templateParameters;
060
061    public CPPType(Type ownerType, Type rawType, Object... templateParameters) {
062        this.ownerType = ownerType;
063        this.templateParameters = templateParameters == null ? new Object[0] : templateParameters;
064        this.actualTypeArguments = getTypes(this.templateParameters);
065        this.rawType = rawType;
066    }
067
068    public CPPType(Type rawType, Object... templateParameters) {
069        this(null, rawType, templateParameters);
070    }
071
072    private static Type[] getTypes(Object[] objects) {
073//        assert objects != null;
074        int n = objects == null ? 0 : objects.length;
075        List<Type> ret = new ArrayList<Type>(n);
076        for (int i = 0; i < n; i++) {
077            Object o = objects[i];
078            if (o instanceof Type) {
079                ret.add((Type) o);
080            }
081        }
082        return ret.toArray(new Type[ret.size()]);
083    }
084
085    static Object[] cons(Class firstClass, Object... flattenedClassesAndParams) {
086        Object[] a = new Object[flattenedClassesAndParams.length + 1];
087        a[0] = firstClass;
088        System.arraycopy(flattenedClassesAndParams, 0, a, 1, flattenedClassesAndParams.length);
089        return a;
090    }
091
092    public static Type getCPPType(Object... flattenedClassesAndParams) {
093        int[] position = new int[]{0};
094        Type t = parseCPPType(flattenedClassesAndParams, position);
095        if (position[0] < flattenedClassesAndParams.length) {
096            parseError("Unexpected trailing parameters", flattenedClassesAndParams, position);
097        }
098
099        return t;
100    }
101
102    static void parseError(String message, Object[] flattenedClassesAndParams, int[] position) {
103        throw new IllegalArgumentException("Error while parsing C++ type in " + Arrays.asList(flattenedClassesAndParams) + " at offset " + position[0] + " : " + message);
104    }
105
106    static void notEOF(String message, Object[] flattenedClassesAndParams, int[] position) {
107        if (position[0] >= flattenedClassesAndParams.length) {
108            throw new IllegalArgumentException("EOF while parsing C++ type in " + Arrays.asList(flattenedClassesAndParams) + " at offset " + position[0] + " : " + message);
109        }
110    }
111
112    static Type parseCPPType(Object[] flattenedClassesAndParams, int[] position) {
113        notEOF("expecting class", flattenedClassesAndParams, position);
114        Object oc = flattenedClassesAndParams[position[0]];
115        if (!(oc instanceof Class)) {
116            parseError("expected class", flattenedClassesAndParams, position);
117        }
118        Class<?> c = (Class) oc;
119        position[0]++;
120        Template t = c.getAnnotation(Template.class);
121
122        Class<?>[] paramTypes = t == null ? null : t.value();
123        int nParams = paramTypes == null ? 0 : paramTypes.length;
124        Object[] params = new Object[nParams];
125        for (int iParam = 0; iParam < nParams; iParam++) {
126            notEOF("expecting param " + iParam + " for template " + c.getName(), flattenedClassesAndParams, position);
127            Object param = flattenedClassesAndParams[position[0]];
128            Type paramType = paramTypes[iParam];
129            if (paramType.equals(Class.class) && param.getClass().equals(Class.class)) {
130                param = parseCPPType(flattenedClassesAndParams, position);
131            } else {
132                if (!((Class) paramType).isInstance(param)) {
133                    parseError("bad type for template param " + iParam + " : expected a " + paramType + ", got " + param, flattenedClassesAndParams, position);
134                }
135                position[0]++;
136            }
137            params[iParam] = param;
138        }
139        return nParams == 0 ? c : new CPPType(c, params);
140    }
141
142    //@Override
143    public Type[] getActualTypeArguments() {
144        return actualTypeArguments.clone();
145    }
146
147    //@Override
148    public java.lang.reflect.Type getOwnerType() {
149        return ownerType;
150    }
151
152    //@Override
153    public java.lang.reflect.Type getRawType() {
154        return rawType;
155    }
156
157    public Object[] getTemplateParameters() {
158        return templateParameters.clone();
159    }
160
161    @Override
162    public int hashCode() {
163        int h = getRawType().hashCode();
164        if (getOwnerType() != null) {
165            h ^= getOwnerType().hashCode();
166        }
167        for (int i = 0, n = templateParameters.length; i < n; i++) {
168            h ^= templateParameters[i].hashCode();
169        }
170        return h;
171    }
172
173    static boolean eq(Object a, Object b) {
174        if ((a == null) != (b == null)) {
175            return false;
176        }
177        if (a != null && !a.equals(b)) {
178            return false;
179        }
180        return true;
181    }
182
183    @Override
184    public boolean equals(Object o) {
185        if (o == null || !(o instanceof CPPType)) {
186            return false;
187        }
188
189        CPPType t = (CPPType) o;
190        if (!eq(getRawType(), t.getRawType())) {
191            return false;
192        }
193        if (!eq(getOwnerType(), t.getOwnerType())) {
194            return false;
195        }
196
197        Object[] tp = t.templateParameters;
198        if (templateParameters.length != tp.length) {
199            return false;
200        }
201
202        for (int i = 0, n = templateParameters.length; i < n; i++) {
203            if (!eq(templateParameters[i], tp[i])) {
204                return false;
205            }
206        }
207
208        return true;
209    }
210
211    @Override
212    public String toString() {
213        StringBuilder b = new StringBuilder();
214        if (getOwnerType() != null) {
215            b.append(getOwnerType()).append('.');
216        }
217
218        b.append(getRawType());
219        int n = templateParameters.length;
220        if (n != 0) {
221            b.append('<');
222            for (int i = 0; i < n; i++) {
223                if (i > 0) {
224                    b.append(", ");
225                }
226                b.append(templateParameters[i]);
227            }
228            b.append('>');
229        }
230        return b.toString();
231    }
232}