001/*******************************************************************************
002 * Copyright (C) 2009-2011 FuseSource Corp.
003 * Copyright (c) 2004, 2007 IBM Corporation and others.
004 *
005 * All rights reserved. This program and the accompanying materials
006 * are made available under the terms of the Eclipse Public License v1.0
007 * which accompanies this distribution, and is available at
008 * http://www.eclipse.org/legal/epl-v10.html
009 *
010 *******************************************************************************/
011package org.fusesource.hawtjni.generator;
012
013import java.lang.reflect.Modifier;
014import java.util.ArrayList;
015import java.util.List;
016
017import org.fusesource.hawtjni.generator.model.JNIClass;
018import org.fusesource.hawtjni.generator.model.JNIField;
019import org.fusesource.hawtjni.generator.model.JNIType;
020import org.fusesource.hawtjni.runtime.ClassFlag;
021
022/**
023 * 
024 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
025 */
026public class StructsGenerator extends JNIGenerator {
027
028    boolean header;
029
030    static final boolean GLOBAL_REF = false;
031
032    public StructsGenerator(boolean header) {
033        this.header = header;
034    }
035
036    public void generateCopyright() {
037        outputln(fixDelimiter(getCopyright()));
038    }
039
040    public void generateIncludes() {
041        if (header) {
042            outputln("#include \""+getOutputName()+".h\"");
043        } else {
044            outputln("#include \""+getOutputName()+".h\"");
045            outputln("#include \"hawtjni.h\"");
046            outputln("#include \""+getOutputName()+"_structs.h\"");
047        }
048        outputln();
049    }
050
051    public void generate(JNIClass clazz) {
052        ArrayList<JNIField> fields = getStructFields(clazz);
053        if (fields.isEmpty())
054            return;
055        if (header) {
056            generateHeaderFile(clazz);
057        } else {
058            generateSourceFile(clazz);
059        }
060    }
061
062    private ArrayList<JNIField> getStructFields(JNIClass clazz) {
063        ArrayList<JNIField> rc = new ArrayList<JNIField>();
064        List<JNIField> fields = clazz.getDeclaredFields();
065        for (JNIField field : fields) {
066            int mods = field.getModifiers();
067            if ( (mods & Modifier.STATIC) == 0 && (mods & Modifier.TRANSIENT) == 0) {
068                rc.add(field);
069            }
070        }
071        return rc;
072    }
073
074    void generateHeaderFile(JNIClass clazz) {
075        generateSourceStart(clazz);
076        generatePrototypes(clazz);
077        generateBlankMacros(clazz);
078        generateSourceEnd(clazz);
079        outputln();
080    }
081
082    void generateSourceFile(JNIClass clazz) {
083        generateSourceStart(clazz);
084        generateFIDsStructure(clazz);
085        outputln();
086        generateGlobalVar(clazz);
087        outputln();
088        generateFunctions(clazz);
089        generateSourceEnd(clazz);
090        outputln();
091    }
092
093    void generateSourceStart(JNIClass clazz) {
094        String conditional = clazz.getConditional();
095        if (conditional!=null) {
096            outputln("#if "+conditional);
097        }
098    }
099
100    void generateSourceEnd(JNIClass clazz) {
101        if (clazz.getConditional()!=null) {
102            outputln("#endif");
103        }
104    }
105
106    void generateGlobalVar(JNIClass clazz) {
107        String simpleName = clazz.getSimpleName();
108        output(simpleName);
109        output("_FID_CACHE ");
110        output(simpleName);
111        outputln("Fc;");
112    }
113
114    void generateBlankMacros(JNIClass clazz) {
115        
116        if (clazz.getConditional()==null) {
117            return;
118        }
119        
120        String simpleName = clazz.getSimpleName();
121        outputln("#else");
122        output("#define cache");
123        output(simpleName);
124        outputln("Fields(a,b)");
125        output("#define get");
126        output(simpleName);
127        outputln("Fields(a,b,c) NULL");
128        output("#define set");
129        output(simpleName);
130        outputln("Fields(a,b,c)");
131    }
132
133    void generatePrototypes(JNIClass clazz) {
134        String clazzName = clazz.getNativeName();
135        String simpleName = clazz.getSimpleName();
136        output("void cache");
137        output(simpleName);
138        outputln("Fields(JNIEnv *env, jobject lpObject);");
139        if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
140            output("struct ");
141        }
142        output(clazzName);
143        output(" *get");
144        output(simpleName);
145        output("Fields(JNIEnv *env, jobject lpObject, ");
146        if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
147            output("struct ");
148        }
149        output(clazzName);
150        outputln(" *lpStruct);");
151        output("void set");
152        output(simpleName);
153        output("Fields(JNIEnv *env, jobject lpObject, ");
154        if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
155            output("struct ");
156        }
157        output(clazzName);
158        outputln(" *lpStruct);");
159    }
160
161    void generateFIDsStructure(JNIClass clazz) {
162        String simpleName = clazz.getSimpleName();
163        output("typedef struct ");
164        output(simpleName);
165        outputln("_FID_CACHE {");
166        outputln("\tint cached;");
167        outputln("\tjclass clazz;");
168        output("\tjfieldID ");
169        List<JNIField> fields = clazz.getDeclaredFields();
170        boolean first = true;
171        for (JNIField field : fields) {
172            if (ignoreField(field))
173                continue;
174            if (!first)
175                output(", ");
176            output(field.getName());
177            first = false;
178        }
179        outputln(";");
180        output("} ");
181        output(simpleName);
182        outputln("_FID_CACHE;");
183    }
184
185    void generateCacheFunction(JNIClass clazz) {
186        String simpleName = clazz.getSimpleName();
187        String clazzName = clazz.getNativeName();
188        output("void cache");
189        output(simpleName);
190        outputln("Fields(JNIEnv *env, jobject lpObject)");
191        outputln("{");
192        output("\tif (");
193        output(simpleName);
194        outputln("Fc.cached) return;");
195        JNIClass superclazz = clazz.getSuperclass();
196        if (!superclazz.getName().equals("java.lang.Object")) {
197            String superName = superclazz.getSimpleName();
198            output("\tcache");
199            output(superName);
200            outputln("Fields(env, lpObject);");
201        }
202        output("\t");
203        output(simpleName);
204        if (isCPP) {
205            if (GLOBAL_REF) {
206                output("Fc.clazz = (jclass)env->NewGlobalRef(env->GetObjectClass(lpObject));");
207            } else {
208                output("Fc.clazz = env->GetObjectClass(lpObject);");
209            }
210        } else {
211            if (GLOBAL_REF) {
212                output("Fc.clazz = (*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, lpObject));");
213            } else {
214                output("Fc.clazz = (*env)->GetObjectClass(env, lpObject);");
215            }
216        }
217        outputln();
218        List<JNIField> fields = clazz.getDeclaredFields();
219        for (JNIField field : fields) {
220            if (ignoreField(field))
221                continue;
222            output("\t");
223            output(simpleName);
224            output("Fc.");
225            output(field.getName());
226            if (isCPP) {
227                output(" = env->GetFieldID(");
228            } else {
229                output(" = (*env)->GetFieldID(env, ");
230            }
231            output(simpleName);
232            output("Fc.clazz, \"");
233            output(field.getName());
234            JNIType type = field.getType(), type64 = field.getType64();
235            output("\", ");
236            if (type.equals(type64))
237                output("\"");
238            output(type.getTypeSignature(!type.equals(type64)));
239            if (type.equals(type64))
240                output("\"");
241            outputln(");");
242        }
243        // Makes sure compiler/cpu does not reorder the following write before the previous updates are done.
244        outputln("\thawtjni_w_barrier();");
245        output("\t");
246        output(simpleName);
247        outputln("Fc.cached = 1;");
248        outputln("}");
249    }
250
251    void generateGetFields(JNIClass clazz) {
252        JNIClass superclazz = clazz.getSuperclass();
253        String clazzName = clazz.getNativeName();
254        String superName = superclazz.getNativeName();
255        if (!superclazz.getName().equals("java.lang.Object")) {
256            /*
257             * Windows exception - cannot call get/set function of super class
258             * in this case
259             */
260            if (!(clazzName.equals(superName + "A") || clazzName.equals(superName + "W"))) {
261                output("\tget");
262                output(superName);
263                output("Fields(env, lpObject, (");
264                output(superName);
265                outputln(" *)lpStruct);");
266            } else {
267                generateGetFields(superclazz);
268            }
269        }
270        List<JNIField> fields = clazz.getDeclaredFields();
271        for (JNIField field : fields) {
272            if (ignoreField(field))
273                continue;
274            String conditional = field.getConditional();
275            if (conditional!=null) {
276                outputln("#if "+conditional);
277            }
278            JNIType type = field.getType(), type64 = field.getType64();
279            String simpleName = type.getSimpleName();
280            String accessor = field.getAccessor();
281            if (accessor == null || accessor.length() == 0)
282                accessor = field.getName();
283            if (type.isPrimitive()) {
284                output("\tlpStruct->");
285                output(accessor);
286                output(" = ");
287                output(field.getCast());
288                if( field.isPointer() ) {
289                    output("(intptr_t)");
290                }
291                if (isCPP) {
292                    output("env->Get");
293                } else {
294                    output("(*env)->Get");
295                }
296                output(type.getTypeSignature1(!type.equals(type64)));
297                if (isCPP) {
298                    output("Field(lpObject, ");
299                } else {
300                    output("Field(env, lpObject, ");
301                }
302                output(field.getDeclaringClass().getSimpleName());
303                output("Fc.");
304                output(field.getName());
305                output(");");
306            } else if (type.isArray()) {
307                JNIType componentType = type.getComponentType(), componentType64 = type64.getComponentType();
308                if (componentType.isPrimitive()) {
309                    outputln("\t{");
310                    output("\t");
311                    output(type.getTypeSignature2(!type.equals(type64)));
312                    output(" lpObject1 = (");
313                    output(type.getTypeSignature2(!type.equals(type64)));
314                    if (isCPP) {
315                        output(")env->GetObjectField(lpObject, ");
316                    } else {
317                        output(")(*env)->GetObjectField(env, lpObject, ");
318                    }
319                    output(field.getDeclaringClass().getSimpleName());
320                    output("Fc.");
321                    output(field.getName());
322                    outputln(");");
323                    if (isCPP) {
324                        output("\tenv->Get");
325                    } else {
326                        output("\t(*env)->Get");
327                    }
328                    output(componentType.getTypeSignature1(!componentType.equals(componentType64)));
329                    if (isCPP) {
330                        output("ArrayRegion(lpObject1, 0, sizeof(lpStruct->");
331                    } else {
332                        output("ArrayRegion(env, lpObject1, 0, sizeof(lpStruct->");
333                    }
334                    output(accessor);
335                    output(")");
336                    if (!componentType.isType("byte")) {
337                        output(" / sizeof(");
338                        output(componentType.getTypeSignature2(!componentType.equals(componentType64)));
339                        output(")");
340                    }
341                    output(", (");
342                    output(type.getTypeSignature4(!type.equals(type64), false));
343                    output(")lpStruct->");
344                    output(accessor);
345                    outputln(");");
346                    output("\t}");
347                } else {
348                    throw new Error("not done");
349                }
350            } else {
351                outputln("\t{");
352                if (isCPP) {
353                    output("\tjobject lpObject1 = env->GetObjectField(lpObject, ");
354                } else {
355                    output("\tjobject lpObject1 = (*env)->GetObjectField(env, lpObject, ");
356                }
357                output(field.getDeclaringClass().getSimpleName());
358                output("Fc.");
359                output(field.getName());
360                outputln(");");
361                output("\tif (lpObject1 != NULL) get");
362                output(simpleName);
363                output("Fields(env, lpObject1, &lpStruct->");
364                output(accessor);
365                outputln(");");
366                output("\t}");
367            }
368            outputln();
369            if (conditional!=null) {
370                outputln("#endif");
371            }
372        }
373    }
374
375    void generateGetFunction(JNIClass clazz) {
376        String clazzName = clazz.getNativeName();
377        String simpleName = clazz.getSimpleName();
378        if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
379            output("struct ");
380        }
381        output(clazzName);
382        output(" *get");
383        output(simpleName);
384        output("Fields(JNIEnv *env, jobject lpObject, ");
385        if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
386            output("struct ");
387        }
388        output(clazzName);
389        outputln(" *lpStruct)");
390        outputln("{");
391        output("\tif (!");
392        output(simpleName);
393        output("Fc.cached) cache");
394        output(simpleName);
395        outputln("Fields(env, lpObject);");
396        if( clazz.getFlag(ClassFlag.ZERO_OUT) ) {
397            outputln("memset(lpStruct, 0, sizeof(struct "+clazzName+"));");
398        }
399        generateGetFields(clazz);
400        outputln("\treturn lpStruct;");
401        outputln("}");
402    }
403
404    void generateSetFields(JNIClass clazz) {
405        JNIClass superclazz = clazz.getSuperclass();
406        String clazzName = clazz.getNativeName();
407        String superName = superclazz.getNativeName();
408        if (!superclazz.getName().equals("java.lang.Object")) {
409            /*
410             * Windows exception - cannot call get/set function of super class
411             * in this case
412             */
413            if (!(clazzName.equals(superName + "A") || clazzName.equals(superName + "W"))) {
414                output("\tset");
415                output(superName);
416                output("Fields(env, lpObject, (");
417                output(superName);
418                outputln(" *)lpStruct);");
419            } else {
420                generateSetFields(superclazz);
421            }
422        }
423        List<JNIField> fields = clazz.getDeclaredFields();
424        for (JNIField field : fields) {
425            if (ignoreField(field))
426                continue;
427            String conditional = field.getConditional();
428            if (conditional!=null) {
429                outputln("#if "+conditional);
430            }
431            JNIType type = field.getType(), type64 = field.getType64();
432            boolean allowConversion = !type.equals(type64);
433            
434            String simpleName = type.getSimpleName();
435            String accessor = field.getAccessor();
436            if (accessor == null || accessor.length() == 0)
437                accessor = field.getName();
438            if (type.isPrimitive()) {
439                if (isCPP) {
440                    output("\tenv->Set");
441                } else {
442                    output("\t(*env)->Set");
443                }
444                output(type.getTypeSignature1(allowConversion));
445                if (isCPP) {
446                    output("Field(lpObject, ");
447                } else {
448                    output("Field(env, lpObject, ");
449                }
450                output(field.getDeclaringClass().getSimpleName());
451                output("Fc.");
452                output(field.getName());
453                output(", ");
454                output("("+type.getTypeSignature2(allowConversion)+")");
455                if( field.isPointer() ) {
456                    output("(intptr_t)");
457                }
458                output("lpStruct->"+accessor);
459                output(");");
460            } else if (type.isArray()) {
461                JNIType componentType = type.getComponentType(), componentType64 = type64.getComponentType();
462                if (componentType.isPrimitive()) {
463                    outputln("\t{");
464                    output("\t");
465                    output(type.getTypeSignature2(allowConversion));
466                    output(" lpObject1 = (");
467                    output(type.getTypeSignature2(allowConversion));
468                    if (isCPP) {
469                        output(")env->GetObjectField(lpObject, ");
470                    } else {
471                        output(")(*env)->GetObjectField(env, lpObject, ");
472                    }
473                    output(field.getDeclaringClass().getSimpleName());
474                    output("Fc.");
475                    output(field.getName());
476                    outputln(");");
477                    if (isCPP) {
478                        output("\tenv->Set");
479                    } else {
480                        output("\t(*env)->Set");
481                    }
482                    output(componentType.getTypeSignature1(!componentType.equals(componentType64)));
483                    if (isCPP) {
484                        output("ArrayRegion(lpObject1, 0, sizeof(lpStruct->");
485                    } else {
486                        output("ArrayRegion(env, lpObject1, 0, sizeof(lpStruct->");
487                    }
488                    output(accessor);
489                    output(")");
490                    if (!componentType.isType("byte")) {
491                        output(" / sizeof(");
492                        output(componentType.getTypeSignature2(!componentType.equals(componentType64)));
493                        output(")");
494                    }
495                    output(", (");
496                    output(type.getTypeSignature4(allowConversion, false));
497                    output(")lpStruct->");
498                    output(accessor);
499                    outputln(");");
500                    output("\t}");
501                } else {
502                    throw new Error("not done");
503                }
504            } else {
505                outputln("\t{");
506                if (isCPP) {
507                    output("\tjobject lpObject1 = env->GetObjectField(lpObject, ");
508                } else {
509                    output("\tjobject lpObject1 = (*env)->GetObjectField(env, lpObject, ");
510                }
511                output(field.getDeclaringClass().getSimpleName());
512                output("Fc.");
513                output(field.getName());
514                outputln(");");
515                output("\tif (lpObject1 != NULL) set");
516                output(simpleName);
517                output("Fields(env, lpObject1, &lpStruct->");
518                output(accessor);
519                outputln(");");
520                output("\t}");
521            }
522            outputln();
523            if (conditional!=null) {
524                outputln("#endif");
525            }
526        }
527    }
528
529    void generateSetFunction(JNIClass clazz) {
530        String clazzName = clazz.getNativeName();
531        String simpleName = clazz.getSimpleName();
532        output("void set");
533        output(simpleName);
534        output("Fields(JNIEnv *env, jobject lpObject, ");
535        if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
536            output("struct ");
537        }
538        output(clazzName);
539        outputln(" *lpStruct)");
540        outputln("{");
541        output("\tif (!");
542        output(simpleName);
543        output("Fc.cached) cache");
544        output(simpleName);
545        outputln("Fields(env, lpObject);");
546        generateSetFields(clazz);
547        outputln("}");
548    }
549
550    void generateFunctions(JNIClass clazz) {
551        generateCacheFunction(clazz);
552        outputln();
553        generateGetFunction(clazz);
554        outputln();
555        generateSetFunction(clazz);
556    }
557
558    boolean ignoreField(JNIField field) {
559        int mods = field.getModifiers();
560        return field.ignore() || ((mods & Modifier.FINAL) != 0) || ((mods & Modifier.STATIC) != 0);
561    }
562
563}