protected void outputConvertedStruct(
      Struct struct,
      Signatures signatures,
      DeclarationsHolder out,
      Identifier callerLibraryClass,
      String callerLibrary,
      boolean onlyFields)
      throws IOException {
    Struct structJavaClass =
        convertStruct(struct, signatures, callerLibraryClass, callerLibrary, onlyFields);
    if (structJavaClass == null) return;

    if (result.config.putTopStructsInSeparateFiles
        && struct.findParentOfType(Struct.class) == null) {
      String library = result.getLibrary(struct);
      Identifier javaPackage = result.getLibraryPackage(library);
      Identifier fullClassName = ident(javaPackage, structJavaClass.getTag().clone());

      if (result.config.runtime == JNAeratorConfig.Runtime.BridJ)
        structJavaClass.addAnnotation(new Annotation(org.bridj.ann.Library.class, expr(library)));
      structJavaClass.removeModifiers(ModifierType.Static);
      structJavaClass =
          result.notifyBeforeWritingClass(fullClassName, structJavaClass, signatures, library);
      if (structJavaClass != null) {
        PrintWriter pout = result.classOutputter.getClassSourceWriter(fullClassName.toString());
        result.printJavaClass(javaPackage, structJavaClass, pout);
        pout.close();
      }
    } else out.addDeclaration(decl(structJavaClass));
  }
 public void convertCallback(
     FunctionSignature functionSignature,
     Signatures signatures,
     DeclarationsHolder out,
     Identifier callerLibraryName) {
   Struct decl = convertCallback(functionSignature, signatures, callerLibraryName);
   if (decl != null) out.addDeclaration(new TaggedTypeRefDeclaration(decl));
 }
  protected void outputEnumItemsAsConstants(
      List<EnumItemResult> results,
      DeclarationsHolder out,
      Signatures signatures,
      Identifier libraryClassName,
      boolean hasEnumClass) {

    for (EnumItemResult er : results) {
      try {
        if (er.errorElement != null) {
          out.addDeclaration(er.errorElement);
          continue;
        }
        String itemName =
            result.typeConverter.getValidJavaIdentifierString(ident(er.originalItem.getName()));
        Declaration ct =
            outputConstant(
                itemName,
                result.typeConverter.convertExpressionToJava(
                    er.unconvertedValue, libraryClassName, true),
                signatures,
                er.originalItem,
                "enum item",
                libraryClassName,
                hasEnumClass,
                true,
                true,
                true);
        if (!result.config.noComments)
          if (ct != null && hasEnumClass) {
            String c = ct.getCommentBefore();
            ct.setCommentBefore(er.originalItem.getCommentBefore());
            ct.addToCommentBefore(c);
          }
        out.addDeclaration(ct);
      } catch (Exception ex) {
        out.addDeclaration(skipDeclaration(er.originalItem, ex.toString()));
      }
    }
  }
  protected void outputNSString(
      String name,
      String value,
      DeclarationsHolder out,
      Signatures signatures,
      Element... elementsToTakeCommentsFrom) {

    if (!signatures.addVariable(name)) return;

    TypeRef tr = typeRef(String.class);
    VariablesDeclaration vd = new VariablesDeclaration(tr, new DirectDeclarator(name, expr(value)));
    if (!result.config.noComments)
      for (Element e : elementsToTakeCommentsFrom) {
        vd.addToCommentBefore(e.getCommentBefore());
        vd.addToCommentBefore(e.getCommentAfter());
      }
    vd.addModifiers(ModifierType.Public);
    out.addDeclaration(vd);
  }
  public void convertFunction(
      Function function,
      Signatures signatures,
      boolean isCallback,
      final DeclarationsHolder out,
      final Identifier libraryClassName,
      int iConstructor) {
    if (result.config.functionsAccepter != null && !result.config.functionsAccepter.adapt(function))
      return;

    // if (function.findParentOfType(Template))
    String library = result.getLibrary(function);
    Identifier functionName = function.getName();
    boolean isMethod = function.getParentElement() instanceof Struct;
    if (functionName == null || isCallback) {
      if (function.getParentElement() instanceof FunctionSignature)
        functionName = ident(result.config.callbackInvokeMethodName);
      else return;
    }
    if (function.getParentElement() instanceof FriendDeclaration) return;

    String n = functionName.toString();
    if (n.contains("<") || n.startsWith("~")) return;

    if (result.config.beautifyNames) functionName = ident(result.typeConverter.beautify(n, false));

    functionName = result.typeConverter.getValidJavaMethodName(functionName);
    if (functionName == null) return;

    // if (functionName.equals("operator"))
    //    functionName

    String sig = function.computeSignature(SignatureType.JavaStyle);

    DeclarationsHolder objOut =
        result.config.reification && !isCallback && !isMethod
            ? new DeclarationsHolder() {

              @Override
              public void addDeclaration(Declaration d) {
                out.addDeclaration(d);
                if (d instanceof Function) {
                  Function f = (Function) d;
                  List<Arg> args = f.getArgs();
                  List<TypeRef> trs = new ArrayList<TypeRef>(2);
                  trs.add(f.getValueType());
                  if (!args.isEmpty()) trs.add(args.get(0).getValueType());

                  for (TypeRef tr : trs) {
                    if (tr instanceof SimpleTypeRef) {
                      Identifier id = ((SimpleTypeRef) tr).getName();
                      if (result.isFakePointer(id)) {
                        result.addFunctionReifiableInFakePointer(id, libraryClassName, f);
                      }
                    }
                  }
                }
              }

              @Override
              public List<Declaration> getDeclarations() {
                return out.getDeclarations();
              }
            }
            : out;

    try {
      convertFunction(
          function,
          signatures,
          isCallback,
          objOut,
          libraryClassName,
          sig,
          functionName,
          library,
          iConstructor);
    } catch (UnsupportedConversionException ex) {
      Declaration d = skipDeclaration(function);
      if (d != null) {
        d.addToCommentBefore(ex.toString());
        out.addDeclaration(d);
      }
    }
  }
  public void convertConstants(
      String library,
      List<Define> defines,
      Element sourcesRoot,
      final Signatures signatures,
      final DeclarationsHolder out,
      final Identifier libraryClassName) {
    // final List<Define> defines = new ArrayList<Define>();
    final Map<String, String> constants = Result.getMap(result.stringConstants, library);
    //
    sourcesRoot.accept(
        new Scanner() {
          //			@Override
          //			public void visitDefine(Define define) {
          //				super.visitDefine(define);
          //				if (elementsFilter.accept(define))
          //					defines.add(define);
          //			}
          @Override
          public void visitVariablesDeclaration(VariablesDeclaration v) {
            super.visitVariablesDeclaration(v);
            // if (!elementsFilter.accept(v))
            //	return;

            if (v.findParentOfType(Struct.class) != null) return;

            if (v.getValueType() instanceof FunctionSignature) return;

            for (Declarator decl : v.getDeclarators()) {
              if (!(decl instanceof DirectDeclarator))
                continue; // TODO provide a mapping of exported values

              TypeRef mutatedType = (TypeRef) decl.mutateTypeKeepingParent(v.getValueType());
              if (mutatedType == null
                  || !mutatedType.getModifiers().contains(ModifierType.Const)
                  || mutatedType.getModifiers().contains(ModifierType.Extern)
                  || decl.getDefaultValue() == null) continue;

              // TypeRef type = v.getValueType();
              String name = decl.resolveName();

              JavaPrim prim = result.typeConverter.getPrimitive(mutatedType, libraryClassName);
              if (prim == null) {
                if (mutatedType.toString().contains("NSString")) {
                  String value = constants.get(name);
                  if (value != null) outputNSString(name, value, out, signatures, v, decl);
                }
                continue;
              }

              try {

                // DirectDeclarator dd = (DirectDeclarator)decl;
                Pair<Expression, TypeRef> val =
                    result.typeConverter.convertExpressionToJava(
                        decl.getDefaultValue(), libraryClassName, true);

                if (!signatures.addVariable(name)) continue;

                // TODO
                TypeRef tr =
                    prim == JavaPrim.NativeLong || prim == JavaPrim.NativeSize
                        ? typeRef("long")
                        : primRef(prim)
                    // result.typeConverter.convertTypeToJNA(mutatedType,
                    // TypeConversion.TypeConversionMode.FieldType, libraryClassName)
                    ;
                VariablesDeclaration vd =
                    new VariablesDeclaration(tr, new DirectDeclarator(name, val.getFirst()));
                if (!result.config.noComments) {
                  vd.setCommentBefore(v.getCommentBefore());
                  vd.addToCommentBefore(decl.getCommentBefore());
                  vd.addToCommentBefore(decl.getCommentAfter());
                  vd.addToCommentBefore(v.getCommentAfter());
                }

                //                        if (result.config.runtime ==
                // JNAeratorConfig.Runtime.BridJ)
                vd.addModifiers(ModifierType.Public, ModifierType.Static, ModifierType.Final);

                out.addDeclaration(vd);
              } catch (UnsupportedConversionException e) {
                out.addDeclaration(skipDeclaration(v, e.toString()));
              }
            }
          }
        });

    if (defines != null) {
      for (Define define : reorderDefines(defines)) {
        if (define.getValue() == null) continue;

        try {
          // System.out.println("Define " + define.getName() + " = " + define.getValue());
          out.addDeclaration(
              outputConstant(
                  define.getName(),
                  define.getValue(),
                  signatures,
                  define.getValue(),
                  "define",
                  libraryClassName,
                  true,
                  false,
                  false));
        } catch (UnsupportedConversionException ex) {
          out.addDeclaration(skipDeclaration(define, ex.toString()));
        }
      }
    }
    for (Map.Entry<String, String> e : constants.entrySet()) {
      outputNSString(e.getKey(), e.getValue(), out, signatures);
    }
  }