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     *******************************************************************************/
011    package org.fusesource.hawtjni.generator;
012    
013    import java.lang.reflect.Modifier;
014    import java.util.ArrayList;
015    import java.util.List;
016    
017    import org.fusesource.hawtjni.generator.model.JNIClass;
018    import org.fusesource.hawtjni.generator.model.JNIField;
019    import org.fusesource.hawtjni.generator.model.JNIType;
020    import org.fusesource.hawtjni.runtime.ClassFlag;
021    
022    /**
023     * 
024     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
025     */
026    public 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            output("\t");
244            output(simpleName);
245            outputln("Fc.cached = 1;");
246            outputln("}");
247        }
248    
249        void generateGetFields(JNIClass clazz) {
250            JNIClass superclazz = clazz.getSuperclass();
251            String clazzName = clazz.getNativeName();
252            String superName = superclazz.getNativeName();
253            if (!superclazz.getName().equals("java.lang.Object")) {
254                /*
255                 * Windows exception - cannot call get/set function of super class
256                 * in this case
257                 */
258                if (!(clazzName.equals(superName + "A") || clazzName.equals(superName + "W"))) {
259                    output("\tget");
260                    output(superName);
261                    output("Fields(env, lpObject, (");
262                    output(superName);
263                    outputln(" *)lpStruct);");
264                } else {
265                    generateGetFields(superclazz);
266                }
267            }
268            List<JNIField> fields = clazz.getDeclaredFields();
269            for (JNIField field : fields) {
270                if (ignoreField(field))
271                    continue;
272                String conditional = field.getConditional();
273                if (conditional!=null) {
274                    outputln("#if "+conditional);
275                }
276                JNIType type = field.getType(), type64 = field.getType64();
277                String simpleName = type.getSimpleName();
278                String accessor = field.getAccessor();
279                if (accessor == null || accessor.length() == 0)
280                    accessor = field.getName();
281                if (type.isPrimitive()) {
282                    output("\tlpStruct->");
283                    output(accessor);
284                    output(" = ");
285                    output(field.getCast());
286                    if( field.isPointer() ) {
287                        output("(intptr_t)");
288                    }
289                    if (isCPP) {
290                        output("env->Get");
291                    } else {
292                        output("(*env)->Get");
293                    }
294                    output(type.getTypeSignature1(!type.equals(type64)));
295                    if (isCPP) {
296                        output("Field(lpObject, ");
297                    } else {
298                        output("Field(env, lpObject, ");
299                    }
300                    output(field.getDeclaringClass().getSimpleName());
301                    output("Fc.");
302                    output(field.getName());
303                    output(");");
304                } else if (type.isArray()) {
305                    JNIType componentType = type.getComponentType(), componentType64 = type64.getComponentType();
306                    if (componentType.isPrimitive()) {
307                        outputln("\t{");
308                        output("\t");
309                        output(type.getTypeSignature2(!type.equals(type64)));
310                        output(" lpObject1 = (");
311                        output(type.getTypeSignature2(!type.equals(type64)));
312                        if (isCPP) {
313                            output(")env->GetObjectField(lpObject, ");
314                        } else {
315                            output(")(*env)->GetObjectField(env, lpObject, ");
316                        }
317                        output(field.getDeclaringClass().getSimpleName());
318                        output("Fc.");
319                        output(field.getName());
320                        outputln(");");
321                        if (isCPP) {
322                            output("\tenv->Get");
323                        } else {
324                            output("\t(*env)->Get");
325                        }
326                        output(componentType.getTypeSignature1(!componentType.equals(componentType64)));
327                        if (isCPP) {
328                            output("ArrayRegion(lpObject1, 0, sizeof(lpStruct->");
329                        } else {
330                            output("ArrayRegion(env, lpObject1, 0, sizeof(lpStruct->");
331                        }
332                        output(accessor);
333                        output(")");
334                        if (!componentType.isType("byte")) {
335                            output(" / sizeof(");
336                            output(componentType.getTypeSignature2(!componentType.equals(componentType64)));
337                            output(")");
338                        }
339                        output(", (");
340                        output(type.getTypeSignature4(!type.equals(type64), false));
341                        output(")lpStruct->");
342                        output(accessor);
343                        outputln(");");
344                        output("\t}");
345                    } else {
346                        throw new Error("not done");
347                    }
348                } else {
349                    outputln("\t{");
350                    if (isCPP) {
351                        output("\tjobject lpObject1 = env->GetObjectField(lpObject, ");
352                    } else {
353                        output("\tjobject lpObject1 = (*env)->GetObjectField(env, lpObject, ");
354                    }
355                    output(field.getDeclaringClass().getSimpleName());
356                    output("Fc.");
357                    output(field.getName());
358                    outputln(");");
359                    output("\tif (lpObject1 != NULL) get");
360                    output(simpleName);
361                    output("Fields(env, lpObject1, &lpStruct->");
362                    output(accessor);
363                    outputln(");");
364                    output("\t}");
365                }
366                outputln();
367                if (conditional!=null) {
368                    outputln("#endif");
369                }
370            }
371        }
372    
373        void generateGetFunction(JNIClass clazz) {
374            String clazzName = clazz.getNativeName();
375            String simpleName = clazz.getSimpleName();
376            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
377                output("struct ");
378            }
379            output(clazzName);
380            output(" *get");
381            output(simpleName);
382            output("Fields(JNIEnv *env, jobject lpObject, ");
383            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
384                output("struct ");
385            }
386            output(clazzName);
387            outputln(" *lpStruct)");
388            outputln("{");
389            output("\tif (!");
390            output(simpleName);
391            output("Fc.cached) cache");
392            output(simpleName);
393            outputln("Fields(env, lpObject);");
394            if( clazz.getFlag(ClassFlag.ZERO_OUT) ) {
395                outputln("memset(lpStruct, 0, sizeof(struct "+clazzName+"));");
396            }
397            generateGetFields(clazz);
398            outputln("\treturn lpStruct;");
399            outputln("}");
400        }
401    
402        void generateSetFields(JNIClass clazz) {
403            JNIClass superclazz = clazz.getSuperclass();
404            String clazzName = clazz.getNativeName();
405            String superName = superclazz.getNativeName();
406            if (!superclazz.getName().equals("java.lang.Object")) {
407                /*
408                 * Windows exception - cannot call get/set function of super class
409                 * in this case
410                 */
411                if (!(clazzName.equals(superName + "A") || clazzName.equals(superName + "W"))) {
412                    output("\tset");
413                    output(superName);
414                    output("Fields(env, lpObject, (");
415                    output(superName);
416                    outputln(" *)lpStruct);");
417                } else {
418                    generateSetFields(superclazz);
419                }
420            }
421            List<JNIField> fields = clazz.getDeclaredFields();
422            for (JNIField field : fields) {
423                if (ignoreField(field))
424                    continue;
425                String conditional = field.getConditional();
426                if (conditional!=null) {
427                    outputln("#if "+conditional);
428                }
429                JNIType type = field.getType(), type64 = field.getType64();
430                boolean allowConversion = !type.equals(type64);
431                
432                String simpleName = type.getSimpleName();
433                String accessor = field.getAccessor();
434                if (accessor == null || accessor.length() == 0)
435                    accessor = field.getName();
436                if (type.isPrimitive()) {
437                    if (isCPP) {
438                        output("\tenv->Set");
439                    } else {
440                        output("\t(*env)->Set");
441                    }
442                    output(type.getTypeSignature1(allowConversion));
443                    if (isCPP) {
444                        output("Field(lpObject, ");
445                    } else {
446                        output("Field(env, lpObject, ");
447                    }
448                    output(field.getDeclaringClass().getSimpleName());
449                    output("Fc.");
450                    output(field.getName());
451                    output(", ");
452                    output("("+type.getTypeSignature2(allowConversion)+")");
453                    if( field.isPointer() ) {
454                        output("(intptr_t)");
455                    }
456                    output("lpStruct->"+accessor);
457                    output(");");
458                } else if (type.isArray()) {
459                    JNIType componentType = type.getComponentType(), componentType64 = type64.getComponentType();
460                    if (componentType.isPrimitive()) {
461                        outputln("\t{");
462                        output("\t");
463                        output(type.getTypeSignature2(allowConversion));
464                        output(" lpObject1 = (");
465                        output(type.getTypeSignature2(allowConversion));
466                        if (isCPP) {
467                            output(")env->GetObjectField(lpObject, ");
468                        } else {
469                            output(")(*env)->GetObjectField(env, lpObject, ");
470                        }
471                        output(field.getDeclaringClass().getSimpleName());
472                        output("Fc.");
473                        output(field.getName());
474                        outputln(");");
475                        if (isCPP) {
476                            output("\tenv->Set");
477                        } else {
478                            output("\t(*env)->Set");
479                        }
480                        output(componentType.getTypeSignature1(!componentType.equals(componentType64)));
481                        if (isCPP) {
482                            output("ArrayRegion(lpObject1, 0, sizeof(lpStruct->");
483                        } else {
484                            output("ArrayRegion(env, lpObject1, 0, sizeof(lpStruct->");
485                        }
486                        output(accessor);
487                        output(")");
488                        if (!componentType.isType("byte")) {
489                            output(" / sizeof(");
490                            output(componentType.getTypeSignature2(!componentType.equals(componentType64)));
491                            output(")");
492                        }
493                        output(", (");
494                        output(type.getTypeSignature4(allowConversion, false));
495                        output(")lpStruct->");
496                        output(accessor);
497                        outputln(");");
498                        output("\t}");
499                    } else {
500                        throw new Error("not done");
501                    }
502                } else {
503                    outputln("\t{");
504                    if (isCPP) {
505                        output("\tjobject lpObject1 = env->GetObjectField(lpObject, ");
506                    } else {
507                        output("\tjobject lpObject1 = (*env)->GetObjectField(env, lpObject, ");
508                    }
509                    output(field.getDeclaringClass().getSimpleName());
510                    output("Fc.");
511                    output(field.getName());
512                    outputln(");");
513                    output("\tif (lpObject1 != NULL) set");
514                    output(simpleName);
515                    output("Fields(env, lpObject1, &lpStruct->");
516                    output(accessor);
517                    outputln(");");
518                    output("\t}");
519                }
520                outputln();
521                if (conditional!=null) {
522                    outputln("#endif");
523                }
524            }
525        }
526    
527        void generateSetFunction(JNIClass clazz) {
528            String clazzName = clazz.getNativeName();
529            String simpleName = clazz.getSimpleName();
530            output("void set");
531            output(simpleName);
532            output("Fields(JNIEnv *env, jobject lpObject, ");
533            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
534                output("struct ");
535            }
536            output(clazzName);
537            outputln(" *lpStruct)");
538            outputln("{");
539            output("\tif (!");
540            output(simpleName);
541            output("Fc.cached) cache");
542            output(simpleName);
543            outputln("Fields(env, lpObject);");
544            generateSetFields(clazz);
545            outputln("}");
546        }
547    
548        void generateFunctions(JNIClass clazz) {
549            generateCacheFunction(clazz);
550            outputln();
551            generateGetFunction(clazz);
552            outputln();
553            generateSetFunction(clazz);
554        }
555    
556        boolean ignoreField(JNIField field) {
557            int mods = field.getModifiers();
558            return field.ignore() || ((mods & Modifier.FINAL) != 0) || ((mods & Modifier.STATIC) != 0);
559        }
560    
561    }