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