1 package com4j.tlbimp; 2 3 import com4j.Com4jObject; 4 import com4j.NativeType; 5 import com4j.tlbimp.def.ICoClassDecl; 6 import com4j.tlbimp.def.IConstant; 7 import com4j.tlbimp.def.IDispInterfaceDecl; 8 import com4j.tlbimp.def.IEnumDecl; 9 import com4j.tlbimp.def.IImplementedInterfaceDecl; 10 import com4j.tlbimp.def.IInterfaceDecl; 11 import com4j.tlbimp.def.IMethod; 12 import com4j.tlbimp.def.IParam; 13 import com4j.tlbimp.def.IPrimitiveType; 14 import com4j.tlbimp.def.IPtrType; 15 import com4j.tlbimp.def.IType; 16 import com4j.tlbimp.def.ITypeDecl; 17 import com4j.tlbimp.def.ITypedefDecl; 18 import com4j.tlbimp.def.IWTypeLib; 19 import com4j.tlbimp.def.TypeKind; 20 import com4j.tlbimp.def.VarType; 21 import com4j.tlbimp.def.ISafeArrayType; 22 23 import java.io.File ; 24 import java.io.IOException ; 25 import java.io.PrintWriter ; 26 import java.util.Date ; 27 import java.util.EnumMap ; 28 import java.util.HashMap ; 29 import java.util.Map ; 30 import java.util.Set ; 31 import java.util.HashSet ; 32 import java.nio.Buffer ; 33 34 37 public final class Generator { 38 private final CodeWriter writer; 39 40 private final ReferenceResolver referenceResolver; 41 42 private final ErrorListener el; 43 44 47 private final Set <TypeLibInfo> generatedTypeLibs = new HashSet <TypeLibInfo>(); 48 49 public Generator( CodeWriter writer, ReferenceResolver resolver, ErrorListener el ) { 50 this.el = el; 51 this.writer = writer; 52 this.referenceResolver = resolver; 53 } 54 55 58 public void generate( IWTypeLib lib ) throws BindingException, IOException { 59 TypeLibInfo tli = getTypeLibInfo(lib); 60 if(generatedTypeLibs.add(tli)) 61 new PackageBinder(tli).generate(); 62 } 63 64 70 public void finish() throws IOException { 71 Map <String ,Set <TypeLibInfo>> byPackage = new HashMap <String ,Set <TypeLibInfo>>(); 72 for( TypeLibInfo tli : generatedTypeLibs ) { 73 Set <TypeLibInfo> s = byPackage.get(tli.packageName); 74 if(s==null) 75 byPackage.put(tli.packageName,s=new HashSet <TypeLibInfo>()); 76 s.add(tli); 77 } 78 79 for( Map.Entry <String ,Set <TypeLibInfo>> e : byPackage.entrySet() ) { 80 TypeLibInfo lib1 = e.getValue().iterator().next(); 81 PackageBinder pb = new PackageBinder(lib1); 82 83 IndentingWriter o = writer.create(new File (lib1.getPackageDir(),"ClassFactory.java")); 85 pb.generateHeader(o); 86 87 printJavadoc("Defines methods to create COM objects",o); 88 o.println("public abstract class ClassFactory {"); 89 o.in(); 90 91 o.println("private ClassFactory() {} // instanciation is not allowed"); 92 o.println(); 93 94 for( TypeLibInfo lib : e.getValue() ) { 95 int len = lib.lib.count(); 96 for( int i=0; i<len; i++ ) { 97 ICoClassDecl t = lib.lib.getType(i).queryInterface(ICoClassDecl.class); 98 if(t==null) continue; 99 if(!t.isCreatable()) continue; 100 101 declareFactoryMethod(o, t); 102 t.dispose(); 103 } 104 } 105 106 o.out(); 107 o.println("}"); 108 o.close(); 109 } 110 } 111 112 113 private class PackageBinder { 114 private final TypeLibInfo lib; 115 116 public PackageBinder(TypeLibInfo lib) { 117 this.lib = lib; 118 } 119 120 public void generate() throws IOException { 121 IWTypeLib tlib = lib.lib; 122 generatePackageHtml(); 123 124 int len = tlib.count(); 125 for( int i=0; i<len; i++ ) { 126 ITypeDecl t = tlib.getType(i); 127 switch(t.getKind()) { 128 case DISPATCH: 129 generate( t.queryInterface(IDispInterfaceDecl.class) ); 130 break; 131 case INTERFACE: 132 generate( t.queryInterface(IInterfaceDecl.class) ); 133 break; 134 case ENUM: 135 generate( t.queryInterface(IEnumDecl.class) ); 136 break; 137 case ALIAS: 138 { 139 break; 143 } 144 } 145 t.dispose(); 146 } 147 } 148 149 150 private void generatePackageHtml() throws IOException { 151 PrintWriter o = writer.create( new File (lib.getPackageDir(),"package.html" ) ); 152 o.println("<html><body>"); 153 o.printf("<h2>%1s</h2>",lib.lib.getName()); 154 o.printf("<p>%1s</p>",lib.lib.getHelpString()); 155 o.println("</html></body>"); 156 o.close(); 157 } 158 159 private void generate( IEnumDecl t ) throws IOException { 160 161 int len = t.countConstants(); 163 IConstant[] cons = new IConstant[len]; 164 165 for( int i=0; i<len; i++ ) 166 cons[i] = t.getConstant(i); 167 168 boolean needComEnum = false; 170 for( int i=0; i<len; i++ ) { 171 if( cons[i].getValue()!=i ) { 172 needComEnum = true; 173 break; 174 } 175 } 176 177 String typeName = lib.getTypeName(t); 179 IndentingWriter o = writer.create( new File (lib.getPackageDir(),typeName+".java" ) ); 180 generateHeader(o); 181 182 printJavadoc(t.getHelpString(), o); 183 184 o.printf("public enum %1s ",typeName); 185 if(needComEnum) 186 o.print("implements ComEnum "); 187 o.println("{"); 188 o.in(); 189 190 for( IConstant con : cons ) { 192 printJavadoc(con.getHelpString(),o); 193 o.print(con.getName()); 194 if(needComEnum) { 195 o.printf("(%1d),",con.getValue()); 196 } else { 197 o.print(", // "); 198 o.print(con.getValue()); 199 } 200 o.println(); 201 } 202 203 if(needComEnum) { 204 o.println(";"); 206 o.println(); 207 o.println("private final int value;"); 208 o.println(typeName+"(int value) { this.value=value; }"); 209 o.println("public int comEnumValue() { return value; }"); 210 } 211 212 o.out(); 213 o.println("}"); 214 215 for( IConstant con : cons) 217 con.dispose(); 218 219 o.close(); 220 } 221 222 private void generate( IInterfaceDecl t ) throws IOException { 223 String typeName = lib.getTypeName(t); 224 IndentingWriter o = writer.create( new File (lib.getPackageDir(),typeName+".java" ) ); 225 generateHeader(o); 226 227 printJavadoc(t.getHelpString(), o); 228 229 boolean hasEnum = false; 230 for( int j=0; j<t.countMethods() && !hasEnum; j++ ) { 231 IMethod m = t.getMethod(j); 232 hasEnum = isEnum(m); 233 } 234 235 236 o.printf("@IID(\"%1s\")",t.getGUID()); 237 o.println(); 238 o.printf("public interface %1s",typeName); 239 if(t.countBaseInterfaces()!=0) { 240 o.print(" extends "); 241 o.beginCommaMode(); 242 for( int i=0; i<t.countBaseInterfaces(); i++ ) { 243 o.comma(); 244 String baseName; 245 try { 246 baseName = getTypeName(t.getBaseInterface(i)); 247 } catch (BindingException e) { 248 e.addContext("interface "+typeName); 249 el.error(e); 250 baseName = "Com4jObject"; 251 } 252 o.print(baseName); 253 } 254 if(hasEnum) { 255 o.comma(); 256 o.print("Iterable<Com4jObject>"); 257 } 258 o.endCommaMode(); 259 } 260 o.println(" {"); 261 o.in(); 262 263 for( int j=0; j<t.countMethods(); j++ ) { 264 IMethod m = t.getMethod(j); 265 try { 266 o.startBuffering(); 267 Generator.this.generate(m,o); 268 o.commit(); 269 } catch( BindingException e ) { 270 o.cancel(); 271 e.addContext("interface "+t.getName()); 272 el.error(e); 273 } 274 m.dispose(); 275 } 276 277 o.out(); 278 o.println("}"); 279 280 o.close(); 281 } 282 283 private void generate( IDispInterfaceDecl t ) throws IOException { 284 if(t.isDual()) 285 generate( t.getVtblInterface() ); 286 else { 287 } 289 } 295 296 private void generateHeader(IndentingWriter o) { 297 o.println("// GENERATED. DO NOT MODIFY"); 298 if(lib.packageName.length()!=0) { 299 o.printf("package %1s;",lib.packageName); 300 o.println(); 301 o.println(); 302 } 303 304 o.println("import com4j.*;"); 305 o.println(); 306 } 307 } 308 309 312 private ITypeDecl getDefaultInterface( ICoClassDecl t ) { 313 final int count = t.countImplementedInterfaces(); 314 for( int i=0; i<count; i++ ) { 316 IImplementedInterfaceDecl impl = t.getImplementedInterface(i); 317 if(impl.isSource()) 318 continue; 319 if(impl.isDefault()) 320 return impl.getType(); 321 } 322 323 for( int i=0; i<count; i++ ) { 325 IImplementedInterfaceDecl impl = t.getImplementedInterface(i); 326 if(impl.isSource()) 327 continue; 328 return impl.getType(); 329 } 330 331 return null; 332 } 333 334 private void declareFactoryMethod(IndentingWriter o, ICoClassDecl t) { 335 ITypeDecl p = getDefaultInterface(t); 336 337 String primaryIntf = Com4jObject.class.getName(); try { 339 primaryIntf = getTypeName(p); 340 } catch( BindingException e ) { 341 e.addContext("class factory for coclass "+t.getName()); 342 el.error(e); 343 } 344 345 o.println(); 346 printJavadoc(t.getHelpString(),o); 347 348 o.printf("public static %1s create%2s() {", primaryIntf, t.getName() ); 349 o.println(); 350 o.in(); 351 352 o.printf("return COM4J.createInstance( %1s.class, \"%2s\" );", 353 primaryIntf, t.getGUID()); 354 o.println(); 355 356 o.out(); 357 o.println("}"); 358 } 373 374 375 376 377 378 381 private class MethodBinder { 382 private final IMethod method; 383 private final IParam[] params; 384 private int retParam; 385 386 public MethodBinder(IMethod method) { 387 this.method = method; 388 389 int len = method.getParamCount(); 390 params = new IParam[len]; 391 for( int i=0; i<len; i++ ) 392 params[i] = method.getParam(i); 393 394 retParam = getReturnParam(); 395 } 396 397 401 private int getReturnParam() { 402 for( int i=0; i<params.length; i++ ) { 404 if(params[i].isRetval()) 405 return i; 406 } 407 408 int outIdx=-1; 412 for( int i=0; i<params.length; i++ ) { 413 if(params[i].isOut() && !params[i].isIn()) { 414 if(outIdx==-1) 415 outIdx=i; 416 else 417 return -1; } 419 } 420 421 return outIdx; 422 } 423 424 public void declare( IndentingWriter o ) throws BindingException { 425 printJavadoc(method.getHelpString(), o); 426 o.printf("@VTID(%1d)", 428 method.getVtableIndex()); 429 o.println(); 430 431 if(isEnum(method)) { 432 o.println("java.util.Iterator<Com4jObject> iterator();"); 434 return; 435 } 436 437 declareReturnType(o); 438 439 String name = escape(camelize(method.getName())); 440 if(reservedMethods.contains(name)) 441 name += '_'; 442 o.print(name); 443 o.print('('); 444 o.in(); 445 446 boolean first = true; 447 for( IParam p : params ) { 449 if( retParam!=-1 && p==params[retParam] && !p.isIn() ) 450 continue; if(!first) 452 o.println(','); 453 else 454 o.println(); 455 first = false; 456 declare(p,o); 457 } 458 o.out(); 459 o.println(");"); 460 } 461 462 465 private void declare( IParam p, IndentingWriter o ) throws BindingException { 466 VariableBinding vb = bind(p.getType(),p.getName()); 467 if(!vb.isDefault) { 468 o.printf("@MarshalAs(NativeType.%1s) ",vb.nativeType.name()); 469 } 470 471 String javaType = vb.javaType; 472 473 if(method.isVarArg() && p==params[params.length-1]) { 474 if( javaType.endsWith("[]") ) 476 javaType = javaType.substring(0,javaType.length()-2)+"..."; 477 } 478 o.print(javaType); 479 o.print(' '); 480 String name = p.getName(); 481 if(name==null) name="rhs"; 482 o.print(escape(camelize(name))); 483 } 484 485 488 private void declareReturnType(IndentingWriter o) throws BindingException { 489 if(retParam==-1) { 490 o.print("void "); 491 } else { 492 IPtrType pt = params[retParam].getType().queryInterface(IPtrType.class); 494 if(pt==null) 495 throw new BindingException(Messages.RETVAL_MUST_BY_REFERENCE.format()); 496 VariableBinding vb = bind(pt.getPointedAtType(),null); 497 498 if(!vb.isDefault || params[retParam].isIn() || retParam!=params.length-1 ) { 500 o.print("@ReturnValue("); 501 o.beginCommaMode(); 502 if(!vb.isDefault) { 503 o.comma(); 504 o.print("type=NativeType."+vb.nativeType.name()); 505 } 506 if(params[retParam].isIn()) { 507 o.comma(); 508 o.print("inout=true"); 509 } 510 if(retParam!=params.length-1) { 511 o.comma();; 512 o.print("index="+retParam); 513 } 514 o.endCommaMode(); 515 o.println(")"); 516 } 517 518 o.print(vb.javaType); 519 o.print(' '); 520 } 521 } 522 } 523 524 527 private final Map <IWTypeLib,TypeLibInfo> typeLibs = new HashMap <IWTypeLib,TypeLibInfo>(); 528 529 541 private class TypeLibInfo { 542 final IWTypeLib lib; 543 544 548 final String packageName; 549 550 554 private final Map <ITypeDecl,String > aliases = new HashMap <ITypeDecl,String >(); 555 556 public TypeLibInfo(IWTypeLib lib) throws BindingException { 557 this.lib = lib; 558 this.packageName = referenceResolver.resolve(lib); 559 560 buildSimpleAliasTable(); 561 } 562 563 574 private void buildSimpleAliasTable() { 575 String prefix = packageName; 576 if(prefix.length()==0) prefix += '.'; 577 578 int len = lib.count(); 579 for( int i=0; i<len; i++ ) { 580 ITypeDecl t = lib.getType(i); 581 if(t.getKind()==TypeKind.ALIAS) { 582 ITypedefDecl typedef = t.queryInterface(ITypedefDecl.class); 583 ITypeDecl def = typedef.getDefinition().queryInterface(ITypeDecl.class); 584 if(def!=null) { 585 aliases.put( def, typedef.getName() ); 587 } 588 } 589 t.dispose(); 590 } 591 } 592 593 private File getPackageDir() { 594 if(packageName.equals("")) 595 return new File ("."); 596 else 597 return new File (packageName.replace('.',File.separatorChar)); 598 } 599 600 private String getTypeName(ITypeDecl decl) { 601 assert decl.getParent().equals(lib); 602 603 String name; 604 if(aliases.containsKey(decl)) 605 name = aliases.get(decl); 606 else 607 name = decl.getName(); 608 if(name.equals("IUnknown") || name.equals("IDispatch")) 609 return "Com4jObject"; 610 else 611 return name; 612 } 613 } 614 615 616 626 private String getTypeName(ITypeDecl decl) throws BindingException { 627 return getTypeLibInfo(decl.getParent()).getTypeName(decl); 628 } 629 630 634 private TypeLibInfo getTypeLibInfo(IWTypeLib p) throws BindingException { 635 TypeLibInfo tli = typeLibs.get(p); 636 if(tli==null) { 637 typeLibs.put(p,tli=new TypeLibInfo(p)); 638 } 639 return tli; 640 } 641 642 643 644 645 private static class VariableBinding { 646 public final String javaType; 647 public final NativeType nativeType; 648 652 public final boolean isDefault; 653 654 public VariableBinding(Class javaType, NativeType nativeType, boolean isDefault) { 655 this(javaType.getName(),nativeType,isDefault); 656 } 657 658 public VariableBinding(String javaType, NativeType nativeType, boolean isDefault) { 659 this.javaType = javaType; 660 this.nativeType = nativeType; 661 this.isDefault = isDefault; 662 } 663 664 public VariableBinding createByRef() { 665 String t = javaType; 666 if(boxTypeMap.containsKey(t)) 667 t = boxTypeMap.get(t); 668 return new VariableBinding( "Holder<"+t+">", nativeType.byRef(), isDefault ); 669 } 670 671 private static final Map <String ,String > boxTypeMap = new HashMap <String ,String >(); 672 673 static { 674 boxTypeMap.put("byte","Byte"); 675 boxTypeMap.put("short","Short"); 676 boxTypeMap.put("int","Integer"); 677 boxTypeMap.put("long","Long"); 678 boxTypeMap.put("float","Float"); 679 boxTypeMap.put("double","Double"); 680 boxTypeMap.put("boolean","Boolean"); 681 boxTypeMap.put("char","Character"); 682 } 683 } 684 685 688 private static final Map <VarType,VariableBinding> primitiveTypeBindings 689 = new EnumMap <VarType,VariableBinding>(VarType.class); 690 691 static { 692 pbind( VarType.VT_I2, Short.TYPE, NativeType.Int16, true ); 694 pbind( VarType.VT_I4, Integer.TYPE, NativeType.Int32, true ); 695 pbind( VarType.VT_BSTR, String .class, NativeType.BSTR, true ); 696 pbind( VarType.VT_LPSTR, String .class, NativeType.CSTR, false ); 697 pbind( VarType.VT_LPWSTR, String .class, NativeType.Unicode, false ); 698 pbind( VarType.VT_UI1, Byte.TYPE, NativeType.Int8, true ); 700 pbind( VarType.VT_UI2, Short.TYPE, NativeType.Int16, true ); 701 pbind( VarType.VT_UI4, Integer.TYPE, NativeType.Int32, true ); 702 pbind( VarType.VT_INT, Integer.TYPE, NativeType.Int32, true ); 703 pbind( VarType.VT_UINT, Integer.TYPE, NativeType.Int32, true ); 704 pbind( VarType.VT_BOOL, Boolean.TYPE, NativeType.VariantBool, true ); 705 pbind( VarType.VT_R4, Float.TYPE, NativeType.Float, true ); 706 pbind( VarType.VT_R8, Double.TYPE, NativeType.Double, true ); 707 pbind( VarType.VT_VARIANT, Object .class, NativeType.VARIANT_ByRef, true ); 708 pbind( VarType.VT_DISPATCH, Com4jObject.class, NativeType.Dispatch, false ); 709 pbind( VarType.VT_UNKNOWN, Com4jObject.class, NativeType.ComObject, true ); 710 pbind( VarType.VT_DATE, Date .class, NativeType.Date, true ); 711 } 715 716 private static void pbind( VarType vt, Class c, NativeType n, boolean isDefault ) { 717 primitiveTypeBindings.put(vt,new VariableBinding(c,n,isDefault)); 718 } 719 720 private static final boolean isPsz( String hint ) { 721 if(hint==null) return false; 722 return hint.startsWith("psz") || hint.startsWith("lpsz"); 723 } 724 725 733 private VariableBinding bind( IType t, String nameHint ) throws BindingException { 734 IPrimitiveType pt = t.queryInterface(IPrimitiveType.class); 735 if(pt!=null) { 736 VariableBinding r = primitiveTypeBindings.get(pt.getVarType()); 738 if(r!=null) return r; 739 740 throw new BindingException(Messages.UNSUPPORTED_VARTYPE.format(pt.getVarType())); 741 } 742 743 IPtrType ptrt = t.queryInterface(IPtrType.class); 744 if(ptrt!=null) { 745 IType comp = ptrt.getPointedAtType(); 747 IInterfaceDecl compDecl = comp.queryInterface(IInterfaceDecl.class); 748 if( compDecl!=null ) { 749 return new VariableBinding( getTypeName(compDecl), NativeType.ComObject, true ); 751 } 752 753 IDispInterfaceDecl dispDecl = comp.queryInterface(IDispInterfaceDecl.class); 754 if( dispDecl!=null ) { 755 return new VariableBinding( getTypeName(dispDecl), NativeType.ComObject, true ); 757 } 758 759 IPrimitiveType compPrim = comp.queryInterface(IPrimitiveType.class); 760 if( compPrim!=null ) { 761 if( compPrim.getVarType()==VarType.VT_VARIANT ) { 762 return new VariableBinding(Object .class, NativeType.VARIANT_ByRef, true); 764 } 765 if( compPrim.getVarType()==VarType.VT_VOID ) { 766 return new VariableBinding(Buffer .class, NativeType.PVOID, true ); 768 } 769 if( compPrim.getVarType()==VarType.VT_UI2 ) { 770 if( isPsz(nameHint) ) 772 return new VariableBinding( String .class, NativeType.Unicode, false ); 774 } 775 } 776 777 String name = getTypeString(ptrt); 779 if( name.equals("_RemotableHandle*") ) { 780 return new VariableBinding( Integer.TYPE, NativeType.Int32, true ); 782 } 783 if(name.equals("GUID*")) { 784 return new VariableBinding( "GUID", NativeType.GUID, true ); 785 } 786 787 VariableBinding b = bind(comp,null); 789 if(b!=null && b.nativeType.byRef()!=null ) 790 return b.createByRef(); 791 } 792 793 ISafeArrayType at = t.queryInterface(ISafeArrayType.class); 794 if(at!=null) { 795 IType comp = at.getComponentType(); 796 797 IPrimitiveType compPrim = comp.queryInterface(IPrimitiveType.class); 798 if( compPrim!=null ) { 799 VariableBinding r = primitiveTypeBindings.get(compPrim.getVarType()); 800 if(r!=null) { 801 return new VariableBinding(r.javaType+"[]", NativeType.SafeArray, true ); 802 } 803 } 804 } 805 806 ITypedefDecl typedef = t.queryInterface(ITypedefDecl.class); 808 if(typedef!=null) { 809 return bind(typedef.getDefinition(),nameHint); 810 } 811 812 IEnumDecl enumdef = t.queryInterface(IEnumDecl.class); 814 if(enumdef!=null) { 815 return new VariableBinding( getTypeName(enumdef), NativeType.Int32, true ); 818 } 819 820 ITypeDecl declt = t.queryInterface(ITypeDecl.class); 821 if(declt!=null) { 822 throw new BindingException(Messages.UNSUPPORTED_TYPE.format(getTypeString(t))); 824 } 825 826 throw new BindingException(Messages.UNSUPPORTED_TYPE.format(getTypeString(t))); 827 } 828 829 830 private void generate(IMethod m, IndentingWriter o) throws BindingException { 831 try { 832 new MethodBinder(m).declare(o); 833 o.println(); 834 } catch (BindingException e) { 835 e.addContext("method "+m.getName()); 836 throw e; 837 } 838 } 839 840 private void printJavadoc(String doc, PrintWriter o) { 841 if(doc!=null) { 842 o.println("/**"); 843 o.println(" * "+doc); 844 o.println(" */"); 845 } 846 } 847 848 854 private String getTypeString(IType t) { 855 if(t==null) 856 return "null"; 857 858 IPtrType pt = t.queryInterface(IPtrType.class); 859 if(pt!=null) 860 return getTypeString(pt.getPointedAtType())+"*"; 861 862 IPrimitiveType prim = t.queryInterface(IPrimitiveType.class); 863 if(prim!=null) 864 return prim.getName(); 865 866 ITypeDecl decl = t.queryInterface(ITypeDecl.class); 867 if(decl!=null) 868 return decl.getName(); 869 870 return "N/A"; 871 } 872 873 private static String camelize(String s) { 874 if(Character.isUpperCase(s.charAt(0))) { 875 return Character.toLowerCase(s.charAt(0))+s.substring(1); 877 } else { 878 return s; 879 } 880 } 881 882 private static String escape(String s) { 883 return Escape.escape(s); 884 } 885 886 private static final Set <String > reservedMethods = new HashSet <String >(); 887 888 static { 889 reservedMethods.add("equals"); 890 reservedMethods.add("getClass"); 891 reservedMethods.add("hashCode"); 892 reservedMethods.add("notify"); 893 reservedMethods.add("notifyAll"); 894 reservedMethods.add("toString"); 895 reservedMethods.add("wait"); 896 } 897 898 private static boolean isEnum(IMethod m) { 899 return m.getName().equals("_NewEnum"); 900 } 901 902 } 903 | Popular Tags |