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 Pair<List<VariablesDeclaration>, List<VariablesDeclaration>> getParentAndOwnDeclarations(
      Struct structJavaClass, Struct nativeStruct) throws IOException {
    Pair<List<VariablesDeclaration>, List<VariablesDeclaration>> ret =
        new Pair<List<VariablesDeclaration>, List<VariablesDeclaration>>(
            new ArrayList<VariablesDeclaration>(), new ArrayList<VariablesDeclaration>());
    if (!nativeStruct.getParents().isEmpty()) {
      for (SimpleTypeRef parentName : nativeStruct.getParents()) {
        Struct parent = result.structsByName.get(parentName.getName());
        if (parent == null) {
          // TODO report error
          continue;
        }
        Struct parentJavaClass = convertStruct(parent, new Signatures(), null, null, true);
        Pair<List<VariablesDeclaration>, List<VariablesDeclaration>> parentDecls =
            getParentAndOwnDeclarations(parentJavaClass, parent);
        ret.getFirst().addAll(parentDecls.getFirst());
        ret.getFirst().addAll(parentDecls.getSecond());
      }
    }
    for (Declaration d : structJavaClass.getDeclarations()) {
      if (!(d instanceof VariablesDeclaration)) continue;
      VariablesDeclaration vd = (VariablesDeclaration) d;
      if (vd.getDeclarators().size() != 1) continue; // should not happen !
      if (!isField(vd)) continue;

      ret.getSecond().add(vd);
    }

    return ret;
  }
  public void convertStructs(
      List<Struct> structs,
      Signatures signatures,
      DeclarationsHolder out,
      Identifier libraryClassName,
      String library)
      throws IOException {
    if (structs != null) {
      for (Struct struct : structs) {
        if (struct.findParentOfType(Struct.class) != null) continue;

        if (!result.config.genCPlusPlus && struct.getType().isCpp()) continue;

        outputConvertedStruct(struct, signatures, out, libraryClassName, library, false);
      }
    }
  }
  public int countFieldsInStruct(Struct s) throws UnsupportedConversionException {
    int count = 0;
    for (Declaration declaration : s.getDeclarations()) {
      if (declaration instanceof VariablesDeclaration) {
        count += ((VariablesDeclaration) declaration).getDeclarators().size();
      }
    }
    for (SimpleTypeRef parentName : s.getParents()) {
      Struct parent = result.structsByName.get(parentName.getName());
      if (parent == null)
        throw new UnsupportedConversionException(
            s, "Cannot find parent " + parentName + " of struct " + s);

      count += countFieldsInStruct(parent);
    }
    return count;
  }
 String getFileCommentContent(File file, Element e) {
   if (file != null) {
     String path = result.config.relativizeFileForSourceComments(file.getAbsolutePath());
     String inCategoryStr = "";
     if (e instanceof Function) {
       Function fc = (Function) e;
       Struct parent;
       if (fc.getType() == Type.ObjCMethod
           && ((parent = as(fc.getParentElement(), Struct.class)) != null)
           && (parent.getCategoryName() != null)) {
         inCategoryStr = "from " + parent.getCategoryName() + " ";
       }
     }
     return "<i>"
         + inCategoryStr
         + "native declaration : "
         + path
         + (e == null || e.getElementLine() < 0 ? "" : ":" + e.getElementLine())
         + "</i>";
   } else if (e != null && e.getElementLine() >= 0) {
     return "<i>native declaration : <input>:" + e.getElementLine() + "</i>";
   }
   return null;
 }
  Struct publicStaticClass(
      Identifier name,
      Identifier parentName,
      Struct.Type type,
      Element toCloneCommentsFrom,
      Identifier... interfaces) {
    Struct cl = new Struct();
    cl.setType(type);
    cl.setTag(name);
    if (parentName != null) cl.setParents(typeRef(parentName));
    if (type == Struct.Type.JavaInterface)
      for (Identifier inter : interfaces) cl.addParent(typeRef(inter));
    else for (Identifier inter : interfaces) cl.addProtocol(typeRef(inter));

    if (!result.config.noComments)
      cl.importComments(toCloneCommentsFrom, getFileCommentContent(toCloneCommentsFrom));

    cl.addModifiers(ModifierType.Public, ModifierType.Static);
    return cl;
  }
  public Struct convertCallback(
      FunctionSignature functionSignature, Signatures signatures, Identifier callerLibraryName) {
    Identifier name =
        result.typeConverter.inferCallBackName(functionSignature, true, false, callerLibraryName);
    if (name == null) return null;

    name = result.typeConverter.getValidJavaArgumentName(name);

    Function function = functionSignature.getFunction();

    int i = 1;
    Identifier chosenName = name;
    while (!(signatures.addClass(chosenName))) {
      chosenName = ident(name.toString() + (++i));
    }

    Element parent = functionSignature.getParentElement();
    Element comel = parent != null && parent instanceof TypeDef ? parent : functionSignature;

    Struct callbackStruct = new Struct();
    configureCallbackStruct(callbackStruct);
    // callbackStruct.setParents(Arrays.asList(getCallbackType(functionSignature, chosenName)));
    callbackStruct.setTag(ident(chosenName));
    if (!result.config.noComments)
      callbackStruct.addToCommentBefore(
          comel.getCommentBefore(), comel.getCommentAfter(), getFileCommentContent(comel));
    convertFunction(function, new Signatures(), true, callbackStruct, callerLibraryName, -1);
    for (Declaration d : callbackStruct.getDeclarations()) {
      if (d instanceof Function) {
        callbackStruct.addAnnotations(callbackStruct.getAnnotations());
        callbackStruct.setAnnotations(null);
        break;
      }
    }
    return callbackStruct;
  }
  void addConstructor(Struct s, Function f) {
    Identifier structName = getActualTaggedTypeName(s);

    f.setName(structName);
    s.addDeclaration(f);
  }
 public void addMissingMethods(
     Class<?> originalLib, Signatures existingSignatures, Struct outputLib) {
   for (Pair<Function, String> f : getMethodsAndTheirSignatures(originalLib).getFirst())
     if (existingSignatures.addMethod(f.getSecond()))
       outputLib.addDeclaration(f.getFirst().clone());
 }