001    /*******************************************************************************
002     * Copyright (c) 2009 Progress Software, Inc.
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 clazzName = clazz.getSimpleName();
108            output(clazzName);
109            output("_FID_CACHE ");
110            output(clazzName);
111            outputln("Fc;");
112        }
113    
114        void generateBlankMacros(JNIClass clazz) {
115            
116            if (clazz.getConditional()==null) {
117                return;
118            }
119            
120            String clazzName = clazz.getSimpleName();
121            outputln("#else");
122            output("#define cache");
123            output(clazzName);
124            outputln("Fields(a,b)");
125            output("#define get");
126            output(clazzName);
127            outputln("Fields(a,b,c) NULL");
128            output("#define set");
129            output(clazzName);
130            outputln("Fields(a,b,c)");
131        }
132    
133        void generatePrototypes(JNIClass clazz) {
134            String clazzName = clazz.getSimpleName();
135            output("void cache");
136            output(clazzName);
137            outputln("Fields(JNIEnv *env, jobject lpObject);");
138            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
139                output("struct ");
140            }
141            output(clazzName);
142            output(" *get");
143            output(clazzName);
144            output("Fields(JNIEnv *env, jobject lpObject, ");
145            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
146                output("struct ");
147            }
148            output(clazzName);
149            outputln(" *lpStruct);");
150            output("void set");
151            output(clazzName);
152            output("Fields(JNIEnv *env, jobject lpObject, ");
153            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
154                output("struct ");
155            }
156            output(clazzName);
157            outputln(" *lpStruct);");
158        }
159    
160        void generateFIDsStructure(JNIClass clazz) {
161            String clazzName = clazz.getSimpleName();
162            output("typedef struct ");
163            output(clazzName);
164            outputln("_FID_CACHE {");
165            outputln("\tint cached;");
166            outputln("\tjclass clazz;");
167            output("\tjfieldID ");
168            List<JNIField> fields = clazz.getDeclaredFields();
169            boolean first = true;
170            for (JNIField field : fields) {
171                if (ignoreField(field))
172                    continue;
173                if (!first)
174                    output(", ");
175                output(field.getName());
176                first = false;
177            }
178            outputln(";");
179            output("} ");
180            output(clazzName);
181            outputln("_FID_CACHE;");
182        }
183    
184        void generateCacheFunction(JNIClass clazz) {
185            String clazzName = clazz.getSimpleName();
186            output("void cache");
187            output(clazzName);
188            outputln("Fields(JNIEnv *env, jobject lpObject)");
189            outputln("{");
190            output("\tif (");
191            output(clazzName);
192            outputln("Fc.cached) return;");
193            JNIClass superclazz = clazz.getSuperclass();
194            if (!superclazz.getName().equals("java.lang.Object")) {
195                String superName = superclazz.getSimpleName();
196                output("\tcache");
197                output(superName);
198                outputln("Fields(env, lpObject);");
199            }
200            output("\t");
201            output(clazzName);
202            if (isCPP) {
203                if (GLOBAL_REF) {
204                    output("Fc.clazz = (jclass)env->NewGlobalRef(env->GetObjectClass(lpObject));");
205                } else {
206                    output("Fc.clazz = env->GetObjectClass(lpObject);");
207                }
208            } else {
209                if (GLOBAL_REF) {
210                    output("Fc.clazz = (*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, lpObject));");
211                } else {
212                    output("Fc.clazz = (*env)->GetObjectClass(env, lpObject);");
213                }
214            }
215            outputln();
216            List<JNIField> fields = clazz.getDeclaredFields();
217            for (JNIField field : fields) {
218                if (ignoreField(field))
219                    continue;
220                output("\t");
221                output(clazzName);
222                output("Fc.");
223                output(field.getName());
224                if (isCPP) {
225                    output(" = env->GetFieldID(");
226                } else {
227                    output(" = (*env)->GetFieldID(env, ");
228                }
229                output(clazzName);
230                output("Fc.clazz, \"");
231                output(field.getName());
232                JNIType type = field.getType(), type64 = field.getType64();
233                output("\", ");
234                if (type.equals(type64))
235                    output("\"");
236                output(type.getTypeSignature(!type.equals(type64)));
237                if (type.equals(type64))
238                    output("\"");
239                outputln(");");
240            }
241            output("\t");
242            output(clazzName);
243            outputln("Fc.cached = 1;");
244            outputln("}");
245        }
246    
247        void generateGetFields(JNIClass clazz) {
248            JNIClass superclazz = clazz.getSuperclass();
249            String clazzName = clazz.getSimpleName();
250            String superName = superclazz.getSimpleName();
251            if (!superclazz.getName().equals("java.lang.Object")) {
252                /*
253                 * Windows exception - cannot call get/set function of super class
254                 * in this case
255                 */
256                if (!(clazzName.equals(superName + "A") || clazzName.equals(superName + "W"))) {
257                    output("\tget");
258                    output(superName);
259                    output("Fields(env, lpObject, (");
260                    output(superName);
261                    outputln(" *)lpStruct);");
262                } else {
263                    generateGetFields(superclazz);
264                }
265            }
266            List<JNIField> fields = clazz.getDeclaredFields();
267            for (JNIField field : fields) {
268                if (ignoreField(field))
269                    continue;
270                String conditional = field.getConditional();
271                if (conditional!=null) {
272                    outputln("#if "+conditional);
273                }
274                JNIType type = field.getType(), type64 = field.getType64();
275                String typeName = type.getSimpleName();
276                String accessor = field.getAccessor();
277                if (accessor == null || accessor.length() == 0)
278                    accessor = field.getName();
279                if (type.isPrimitive()) {
280                    output("\tlpStruct->");
281                    output(accessor);
282                    output(" = ");
283                    output(field.getCast());
284                    if( field.isPointer() ) {
285                        output("(intptr_t)");
286                    }
287                    if (isCPP) {
288                        output("env->Get");
289                    } else {
290                        output("(*env)->Get");
291                    }
292                    output(type.getTypeSignature1(!type.equals(type64)));
293                    if (isCPP) {
294                        output("Field(lpObject, ");
295                    } else {
296                        output("Field(env, lpObject, ");
297                    }
298                    output(field.getDeclaringClass().getSimpleName());
299                    output("Fc.");
300                    output(field.getName());
301                    output(");");
302                } else if (type.isArray()) {
303                    JNIType componentType = type.getComponentType(), componentType64 = type64.getComponentType();
304                    if (componentType.isPrimitive()) {
305                        outputln("\t{");
306                        output("\t");
307                        output(type.getTypeSignature2(!type.equals(type64)));
308                        output(" lpObject1 = (");
309                        output(type.getTypeSignature2(!type.equals(type64)));
310                        if (isCPP) {
311                            output(")env->GetObjectField(lpObject, ");
312                        } else {
313                            output(")(*env)->GetObjectField(env, lpObject, ");
314                        }
315                        output(field.getDeclaringClass().getSimpleName());
316                        output("Fc.");
317                        output(field.getName());
318                        outputln(");");
319                        if (isCPP) {
320                            output("\tenv->Get");
321                        } else {
322                            output("\t(*env)->Get");
323                        }
324                        output(componentType.getTypeSignature1(!componentType.equals(componentType64)));
325                        if (isCPP) {
326                            output("ArrayRegion(lpObject1, 0, sizeof(lpStruct->");
327                        } else {
328                            output("ArrayRegion(env, lpObject1, 0, sizeof(lpStruct->");
329                        }
330                        output(accessor);
331                        output(")");
332                        if (!componentType.isType("byte")) {
333                            output(" / sizeof(");
334                            output(componentType.getTypeSignature2(!componentType.equals(componentType64)));
335                            output(")");
336                        }
337                        output(", (");
338                        output(type.getTypeSignature4(!type.equals(type64), false));
339                        output(")lpStruct->");
340                        output(accessor);
341                        outputln(");");
342                        output("\t}");
343                    } else {
344                        throw new Error("not done");
345                    }
346                } else {
347                    outputln("\t{");
348                    if (isCPP) {
349                        output("\tjobject lpObject1 = env->GetObjectField(lpObject, ");
350                    } else {
351                        output("\tjobject lpObject1 = (*env)->GetObjectField(env, lpObject, ");
352                    }
353                    output(field.getDeclaringClass().getSimpleName());
354                    output("Fc.");
355                    output(field.getName());
356                    outputln(");");
357                    output("\tif (lpObject1 != NULL) get");
358                    output(typeName);
359                    output("Fields(env, lpObject1, &lpStruct->");
360                    output(accessor);
361                    outputln(");");
362                    output("\t}");
363                }
364                outputln();
365                if (conditional!=null) {
366                    outputln("#endif");
367                }
368            }
369        }
370    
371        void generateGetFunction(JNIClass clazz) {
372            String clazzName = clazz.getSimpleName();
373            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
374                output("struct ");
375            }
376            output(clazzName);
377            output(" *get");
378            output(clazzName);
379            output("Fields(JNIEnv *env, jobject lpObject, ");
380            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
381                output("struct ");
382            }
383            output(clazzName);
384            outputln(" *lpStruct)");
385            outputln("{");
386            output("\tif (!");
387            output(clazzName);
388            output("Fc.cached) cache");
389            output(clazzName);
390            outputln("Fields(env, lpObject);");
391            if( clazz.getFlag(ClassFlag.ZERO_OUT) ) {
392                outputln("memset(lpStruct, 0, sizeof(struct "+clazzName+"));");
393            }
394            generateGetFields(clazz);
395            outputln("\treturn lpStruct;");
396            outputln("}");
397        }
398    
399        void generateSetFields(JNIClass clazz) {
400            JNIClass superclazz = clazz.getSuperclass();
401            String clazzName = clazz.getSimpleName();
402            String superName = superclazz.getSimpleName();
403            if (!superclazz.getName().equals("java.lang.Object")) {
404                /*
405                 * Windows exception - cannot call get/set function of super class
406                 * in this case
407                 */
408                if (!(clazzName.equals(superName + "A") || clazzName.equals(superName + "W"))) {
409                    output("\tset");
410                    output(superName);
411                    output("Fields(env, lpObject, (");
412                    output(superName);
413                    outputln(" *)lpStruct);");
414                } else {
415                    generateSetFields(superclazz);
416                }
417            }
418            List<JNIField> fields = clazz.getDeclaredFields();
419            for (JNIField field : fields) {
420                if (ignoreField(field))
421                    continue;
422                String conditional = field.getConditional();
423                if (conditional!=null) {
424                    outputln("#if "+conditional);
425                }
426                JNIType type = field.getType(), type64 = field.getType64();
427                boolean allowConversion = !type.equals(type64);
428                
429                String typeName = type.getSimpleName();
430                String accessor = field.getAccessor();
431                if (accessor == null || accessor.length() == 0)
432                    accessor = field.getName();
433                if (type.isPrimitive()) {
434                    if (isCPP) {
435                        output("\tenv->Set");
436                    } else {
437                        output("\t(*env)->Set");
438                    }
439                    output(type.getTypeSignature1(allowConversion));
440                    if (isCPP) {
441                        output("Field(lpObject, ");
442                    } else {
443                        output("Field(env, lpObject, ");
444                    }
445                    output(field.getDeclaringClass().getSimpleName());
446                    output("Fc.");
447                    output(field.getName());
448                    output(", ");
449                    output("("+type.getTypeSignature2(allowConversion)+")");
450                    if( field.isPointer() ) {
451                        output("(intptr_t)");
452                    }
453                    output("lpStruct->"+accessor);
454                    output(");");
455                } else if (type.isArray()) {
456                    JNIType componentType = type.getComponentType(), componentType64 = type64.getComponentType();
457                    if (componentType.isPrimitive()) {
458                        outputln("\t{");
459                        output("\t");
460                        output(type.getTypeSignature2(allowConversion));
461                        output(" lpObject1 = (");
462                        output(type.getTypeSignature2(allowConversion));
463                        if (isCPP) {
464                            output(")env->GetObjectField(lpObject, ");
465                        } else {
466                            output(")(*env)->GetObjectField(env, lpObject, ");
467                        }
468                        output(field.getDeclaringClass().getSimpleName());
469                        output("Fc.");
470                        output(field.getName());
471                        outputln(");");
472                        if (isCPP) {
473                            output("\tenv->Set");
474                        } else {
475                            output("\t(*env)->Set");
476                        }
477                        output(componentType.getTypeSignature1(!componentType.equals(componentType64)));
478                        if (isCPP) {
479                            output("ArrayRegion(lpObject1, 0, sizeof(lpStruct->");
480                        } else {
481                            output("ArrayRegion(env, lpObject1, 0, sizeof(lpStruct->");
482                        }
483                        output(accessor);
484                        output(")");
485                        if (!componentType.isType("byte")) {
486                            output(" / sizeof(");
487                            output(componentType.getTypeSignature2(!componentType.equals(componentType64)));
488                            output(")");
489                        }
490                        output(", (");
491                        output(type.getTypeSignature4(allowConversion, false));
492                        output(")lpStruct->");
493                        output(accessor);
494                        outputln(");");
495                        output("\t}");
496                    } else {
497                        throw new Error("not done");
498                    }
499                } else {
500                    outputln("\t{");
501                    output("\tjobject lpObject1 = (*env)->GetObjectField(env, lpObject, ");
502                    output(field.getDeclaringClass().getSimpleName());
503                    output("Fc.");
504                    output(field.getName());
505                    outputln(");");
506                    output("\tif (lpObject1 != NULL) set");
507                    output(typeName);
508                    output("Fields(env, lpObject1, &lpStruct->");
509                    output(accessor);
510                    outputln(");");
511                    output("\t}");
512                }
513                outputln();
514                if (conditional!=null) {
515                    outputln("#endif");
516                }
517            }
518        }
519    
520        void generateSetFunction(JNIClass clazz) {
521            String clazzName = clazz.getSimpleName();
522            output("void set");
523            output(clazzName);
524            output("Fields(JNIEnv *env, jobject lpObject, ");
525            if (clazz.getFlag(ClassFlag.STRUCT) && !clazz.getFlag(ClassFlag.TYPEDEF)) {
526                output("struct ");
527            }
528            output(clazzName);
529            outputln(" *lpStruct)");
530            outputln("{");
531            output("\tif (!");
532            output(clazzName);
533            output("Fc.cached) cache");
534            output(clazzName);
535            outputln("Fields(env, lpObject);");
536            generateSetFields(clazz);
537            outputln("}");
538        }
539    
540        void generateFunctions(JNIClass clazz) {
541            generateCacheFunction(clazz);
542            outputln();
543            generateGetFunction(clazz);
544            outputln();
545            generateSetFunction(clazz);
546        }
547    
548        boolean ignoreField(JNIField field) {
549            int mods = field.getModifiers();
550            return ((mods & Modifier.PUBLIC) == 0) || ((mods & Modifier.FINAL) != 0) || ((mods & Modifier.STATIC) != 0);
551        }
552    
553    }