/* Copyright (c) 2009-2013 Olivier Chafik, All Rights Reserved This file is part of JNAerator (http://jnaerator.googlecode.com/). JNAerator is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. JNAerator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with JNAerator. If not, see <http://www.gnu.org/licenses/>. */ package com.ochafik.lang.jnaerator; import static com.ochafik.lang.SyntaxUtils.as; import com.ochafik.lang.jnaerator.parser.*; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import com.ochafik.lang.jnaerator.parser.Declarator.DirectDeclarator; import com.ochafik.lang.jnaerator.parser.StoredDeclarations.TypeDef; import com.ochafik.lang.jnaerator.parser.TypeRef.FunctionSignature; import com.ochafik.lang.jnaerator.parser.TypeRef.TaggedTypeRef; import com.nativelibs4java.jalico.Pair; import com.ochafik.util.string.StringUtils; import static com.ochafik.lang.jnaerator.parser.ElementsHelper.*; import com.ochafik.lang.jnaerator.parser.Enum; import com.ochafik.lang.jnaerator.parser.Scanner; import java.util.*; public class MissingNamesChooser extends Scanner { final boolean renameFunctionSignatures; public static boolean isNamedFunctionType(TypeRef tr) { if (!(tr instanceof FunctionSignature)) { return false; } FunctionSignature fs = (FunctionSignature) tr; Function f = fs.getFunction(); return f != null && f.getName() != null; } public enum NameGenerationStyle { Java, PreserveCaseAndSeparateByUnderscores } NameGenerationStyle nameGenerationStyle = NameGenerationStyle.PreserveCaseAndSeparateByUnderscores; Result result; public MissingNamesChooser(Result result, boolean renameFunctionSignatures) { this.result = result; this.renameFunctionSignatures = renameFunctionSignatures; } protected boolean treatFunctionSignatureAsPointers() { return result.config.runtime.hasJNA; } public void setNameGenerationStyle(NameGenerationStyle nameGenerationStyle) { this.nameGenerationStyle = nameGenerationStyle; } public String chooseArgNameFromType(TypeRef tr) throws UnsupportedConversionException { if (tr instanceof TypeRef.SimpleTypeRef) { Identifier name = ((TypeRef.SimpleTypeRef) tr).getName(); String out; if (isNull(name)) { out = StringUtils.implode(tr.getModifiers(), ""); out = out.length() > 0 ? out.substring(0, 1) : out; } else { out = name.toString(); } return out; } else if (tr instanceof TypeRef.Pointer) { return chooseArgNameFromType(((TypeRef.Pointer) tr).getTarget()) + "Ptr"; } else if (tr instanceof TypeRef.ArrayRef) { return chooseArgNameFromType(((TypeRef.ArrayRef) tr).getTarget()) + "Arr"; } throw new UnsupportedConversionException(tr, String.valueOf(tr)); } @Override public void visitFunction(Function function) { switch (function.getType()) { case CFunction: case CppMethod: Set<String> names = new TreeSet<String>(); List<Pair<Arg, Integer>> missing = new ArrayList<Pair<Arg, Integer>>(); int i = 0;//, n = function.getArgs().size(); for (Arg arg : function.getArgs()) { String name = arg.getName(); if (name == null && !isNamedFunctionType(arg.getValueType())) { missing.add(new Pair<Arg, Integer>(arg, i)); } else if (name != null) { names.add(name); } i++; } for (Pair<Arg, Integer> p : missing) { i = 1; String base; if (p.getFirst().isVarArg()) { base = "varArgs"; } else { try { base = chooseArgNameFromType(p.getFirst().getValueType()); } catch (UnsupportedConversionException ex) { base = "arg"; } } // if (p.getFirst().getValueType() instanceof TypeRef.SimpleTypeRef) // base = ((TypeRef.SimpleTypeRef)p.getFirst().getValueType()).getName(); // else // base = "arg"; //(n == 1 ? "" : p.getValue()) String name; while (names.contains(name = base + i)) { i++; } names.add(name); p.getFirst().setName(name); } break; } super.visitFunction(function); } static boolean isNull(Identifier i) { return i == null || i.resolveLastSimpleIdentifier() == null || i.resolveLastSimpleIdentifier().getName() == null; } @Override public void visitFunctionSignature(FunctionSignature functionSignature) { Identifier origName = functionSignature.getFunction() == null ? null : functionSignature.getFunction().getName(); Element parent = functionSignature.getParentElement(); if (!renameFunctionSignatures) { super.visitFunctionSignature(functionSignature); if (functionSignature.getParentElement() instanceof Arg) { Arg arg = (Arg) functionSignature.getParentElement(); if (arg.getName() == null) { arg.setName(origName.toString()); functionSignature.getFunction().setName(null); } } return; } if (!chooseNameIfMissing(functionSignature)) { super.visitFunctionSignature(functionSignature); } if ((parent instanceof TypeDef) || (parent instanceof TypeRef.Pointer) && (parent.getParentElement() instanceof TypeDef)) { return; } DeclarationsHolder holder = functionSignature.findParentOfType(DeclarationsHolder.class); Function f = functionSignature.getFunction(); if (holder != null && f != null && !isNull(f.getName())) { Identifier fnameClone = f.getName().clone(); StoredDeclarations d = as(parent, StoredDeclarations.class); if (d != null && d.getDeclarators().isEmpty()) { if (d instanceof VariablesDeclaration) { VariablesDeclaration pvd = (VariablesDeclaration) d; pvd.addDeclarator(new DirectDeclarator((origName == null ? fnameClone : origName).toString())); functionSignature.replaceBy(functionSignatureRef(fnameClone)); } else { d.replaceBy(null); // special case of C++-like struct sub-type definition } } else { functionSignature.replaceBy(functionSignatureRef(fnameClone)); } TypeDef td = new TypeDef(); td.importDetails(functionSignature, true); td.setValueType(functionSignature); td.addDeclarator(new DirectDeclarator(fnameClone.toString())); holder.addDeclaration(td); td.accept(this); } } TypeRef functionSignatureRef(Identifier funSigName) { TypeRef.SimpleTypeRef tr = new TypeRef.SimpleTypeRef(funSigName.clone()); return tr;//treatFunctionSignatureAsPointers() ? tr : new TypeRef.Pointer(tr, Declarator.PointerStyle.Pointer); //result.typeConverter.pointerTypeRef(tr); } static boolean isUnnamed(TaggedTypeRefDeclaration d) { return d != null && d.getTaggedTypeRef() != null && isNull(d.getTaggedTypeRef().getTag()); } Set<Identifier> listChildIdentifiers(DeclarationsHolder h) { List<Identifier> list = new ArrayList<Identifier>(); for (Declaration d : h.getDeclarations()) { if (d instanceof Function) { list.add(((Function) d).getName()); } else if (d instanceof TaggedTypeRefDeclaration) { TaggedTypeRefDeclaration td = (TaggedTypeRefDeclaration) d; TaggedTypeRef tr = td.getTaggedTypeRef(); if (tr != null) { list.add(tr.getTag()); } } else if (d instanceof VariablesDeclaration) { for (Declarator dc : ((VariablesDeclaration) d).getDeclarators()) { list.add(ident(dc.resolveName())); } } } Set<Identifier> ret = new HashSet<Identifier>(); for (Identifier i : list) { if (i != null) { ret.add(i); } } return ret; } List<TaggedTypeRefDeclaration> getUnnamedTaggedTypeRefs(List<Declaration> ds) { List<TaggedTypeRefDeclaration> ret = new ArrayList<TaggedTypeRefDeclaration>(); for (Declaration d : ds) { if (d instanceof TaggedTypeRefDeclaration) { TaggedTypeRefDeclaration td = (TaggedTypeRefDeclaration) d; TaggedTypeRef tr = td.getTaggedTypeRef(); if (tr != null && tr.getTag() == null) { ret.add(td); } } } return ret; } @Override public void visitStruct(Struct struct) { fixUnNamedChildren(struct); super.visitStruct(struct); } //http://stackoverflow.com/questions/2503183/jnaerator-unnamed-union-missing-in-structure private void fixUnNamedChildren(Struct struct) { List<TaggedTypeRefDeclaration> trs = getUnnamedTaggedTypeRefs(struct.getDeclarations()); if (trs.isEmpty()) { return; } Set<Identifier> ids = listChildIdentifiers(struct); for (TaggedTypeRefDeclaration td : trs) { TaggedTypeRef tr = td.getTaggedTypeRef(); if (!(tr instanceof Struct)) { continue; } Struct s = (Struct) tr; switch (s.getType()) { case CStruct: case CUnion: String n = chooseNameSuffix(tr); int i = 1; Identifier fieldName; while (!ids.add(fieldName = ident("field" + i))) { i++; } //tr.setTag(idTag); td.replaceBy(new VariablesDeclaration(tr, new Declarator.DirectDeclarator(fieldName.toString()))); break; } } } @Override public void visitTaggedTypeRef(TaggedTypeRef taggedTypeRef) { super.visitTaggedTypeRef(taggedTypeRef); chooseNameIfMissing(taggedTypeRef); // return; Element parent = taggedTypeRef.getParentElement(); if (!(parent instanceof TaggedTypeRefDeclaration) && !(parent instanceof TypeDef)) { DeclarationsHolder holder = taggedTypeRef.findParentOfType(DeclarationsHolder.class); if (holder != null && holder != taggedTypeRef.getParentElement() && !(parent instanceof DeclarationsHolder)) { TaggedTypeRefDeclaration td = new TaggedTypeRefDeclaration(); if (parent instanceof VariablesDeclaration && ((VariablesDeclaration) parent).getDeclarators().isEmpty()) { taggedTypeRef.importDetails(parent, false); parent.replaceBy(null); } else { TypeRef tr = new TypeRef.SimpleTypeRef(taggedTypeRef.getTag().clone()); for (Modifier mod : taggedTypeRef.getModifiers()) { if (mod.isA(ModifierKind.StorageClassSpecifier)) { tr.addModifiers(mod); } } taggedTypeRef.replaceBy(tr); if (taggedTypeRef instanceof Struct) { tr.setMarkedAsResolved(true); } } td.setTaggedTypeRef(taggedTypeRef); holder.addDeclaration(td); //td.accept(this); } } // // super.visitTaggedTypeRef(taggedTypeRef); // // Element parent = taggedTypeRef.getParentElement(); // boolean unnamed = // ( // parent instanceof TaggedTypeRefDeclaration && isUnnamed((TaggedTypeRefDeclaration)parent) || // parent instanceof VariablesDeclaration && ((VariablesDeclaration)parent).getDeclarators().isEmpty() // ) && // parent.getParentElement() instanceof Struct // ; // // Support (non-standard) unnamed structs and unions : http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/gcc/unnamed-fields.html // if (unnamed) { // String type = null; // if (taggedTypeRef instanceof Struct) { // switch (((Struct)taggedTypeRef).getType()) { // case CStruct: // type = "Struct"; // break; // case CUnion: // type = "Union"; // break; // case CPPClass: // type = "Class"; // break; // } // } // if (type != null) { //// taggedTypeRef.setParentElement(null); // // VariablesDeclaration vd = new VariablesDeclaration(); // vd.setValueType(taggedTypeRef); // String pref; // if (!isNull(taggedTypeRef.getTag())) { // pref = taggedTypeRef.getTag() + "Field"; // pref = pref.substring(0, 1).toLowerCase() + pref.substring(1); // } else { // pref = "unnamed" + type; // int unnamedId = getNextUnnamedId(pref); // if (taggedTypeRef.getTag() == null) // taggedTypeRef.setTag(ident("Unnamed" + type + unnamedId)); // } // vd.addDeclarator(new DirectDeclarator(pref + getNextUnnamedId(pref))); //// taggedTypeRef.accept(this); // parent.replaceBy(vd); //// vd.accept(this); //// return; // } // } // chooseNameIfMissing(taggedTypeRef); // //// Element parent = taggedTypeRef.getParentElement(); // // if (!(parent instanceof TaggedTypeRefDeclaration) && !(parent instanceof TypeDef)) // { // DeclarationsHolder holder = taggedTypeRef.findParentOfType(DeclarationsHolder.class); // if (holder != null && holder != taggedTypeRef.getParentElement() && !(parent instanceof DeclarationsHolder)) { // TaggedTypeRefDeclaration td = new TaggedTypeRefDeclaration(); // if (unnamed) { // String type = null; // if (taggedTypeRef instanceof Struct) { // switch (((Struct)taggedTypeRef).getType()) { // case CStruct: // type = "Struct"; // break; // case CUnion: // type = "Union"; // break; // case CPPClass: // type = "Class"; // break; // } // } // if (type == null) { // taggedTypeRef.importDetails(parent, false); // parent.replaceBy(null); // } else { //// } else if (unnamed && parent instanceof VariablesDeclaration && ((VariablesDeclaration)parent).getDeclarators().isEmpty()) { //// //// taggedTypeRef.importDetails(parent, false); //// parent.replaceBy(null); // // VariablesDeclaration vd = new VariablesDeclaration(); // vd.setValueType(taggedTypeRef); // String pref; // if (!isNull(taggedTypeRef.getTag())) { // pref = taggedTypeRef.getTag() + "Field"; // pref = pref.substring(0, 1).toLowerCase() + pref.substring(1); // } else { // pref = "unnamed" + type; // int unnamedId = getNextUnnamedId(pref); // if (taggedTypeRef.getTag() == null) // taggedTypeRef.setTag(ident("Unnamed" + type + unnamedId)); // } // vd.addDeclarator(new DirectDeclarator(pref + getNextUnnamedId(pref))); //// taggedTypeRef.accept(this); // parent.replaceBy(vd); // vd.accept(this); // return; // } // // } else { // TypeRef tr = new TypeRef.SimpleTypeRef(taggedTypeRef.getTag().clone()); // for (Modifier mod : taggedTypeRef.getModifiers()) { // if (mod.isA(ModifierKind.StorageClassSpecifier)) // tr.addModifiers(mod); // } // taggedTypeRef.replaceBy(tr); // if (taggedTypeRef instanceof Struct) // tr.setMarkedAsResolved(true); // } // // td.setTaggedTypeRef(taggedTypeRef); // holder.addDeclaration(td); // td.accept(this); // } // } } /** * @return true if the functionSignature changed and triggerered * revisitation */ private boolean chooseNameIfMissing(FunctionSignature functionSignature) { Function function = functionSignature.getFunction(); Element parent = functionSignature.getParentElement(); if (function != null && (isNull(function.getName()) || parent instanceof VariablesDeclaration || parent instanceof Arg)) { String name = null; String exact = JNAeratorUtils.getExactTypeDefName(functionSignature); if (exact != null) { name = exact; } else { List<String> ownerNames = JNAeratorUtils.guessOwnerName(function); if (function.getName() != null) { ownerNames.add(function.getName().toString()); } name = chooseName(functionSignature, ownerNames, true); } if (name != null) { function.setName(ident(name)); function.accept(this); return true; } } return false; } Map<String, Integer> nextUnnamedId = new LinkedHashMap<String, Integer>(); /** * @return true if changed and revisited on change results (caller can give * up) */ private boolean chooseNameIfMissing(TaggedTypeRef taggedTypeRef) { // String tag = taggedTypeRef.getTag(); // taggedTypeRef.setTag(result.declarationsConverter.getActualTaggedTypeName(taggedTypeRef)); // if (!SyntaxUtils.equal(tag, taggedTypeRef.getTag())) { // taggedTypeRef.accept(this); // return true; // } //String betterTag = result.declarationsConverter.getActualTaggedTypeName(taggedTypeRef); if (isNull(taggedTypeRef.getTag()) && !(taggedTypeRef.getParentElement() instanceof TaggedTypeRefDeclaration)) { Identifier tag = result.declarationsConverter.getActualTaggedTypeName(taggedTypeRef); if (isNull(tag)) { List<String> ownerNames = JNAeratorUtils.guessOwnerName(taggedTypeRef);//.getParentElement() instanceof StructTypeRef ? struct.getParentElement() : struct); tag = ident(chooseName(taggedTypeRef, ownerNames, true)); } if (!isNull(tag)) { taggedTypeRef.setTag(tag.clone()); // taggedTypeRef.accept(this); return true; } } return false; } private int getNextUnnamedId(String type) { Integer i = nextUnnamedId.get(type); int unnamedId; if (i == null) { unnamedId = 1; } else { unnamedId = i; } nextUnnamedId.put(type, unnamedId + 1); return unnamedId; } int nextAnonymous = 1; public String chooseName(Element e, List<String> ownerNames, boolean isType) { String s = chooseNameSuffix(e); if (s == null) { return null; } String n; List<String> names = new ArrayList<String>(); if (ownerNames != null) { names.addAll(ownerNames); } if (ownerNames.isEmpty()) { n = s + (nextAnonymous++); } else { names.add(s); switch (nameGenerationStyle) { case Java: n = StringUtils.capitalize(ownerNames, ""); break; case PreserveCaseAndSeparateByUnderscores: n = StringUtils.implode(names, "_"); break; default: throw new UnsupportedOperationException("Unknown name generation style " + nameGenerationStyle); } } if (result.config.beautifyNames) { n = result.typeConverter.beautify(n, isType); } return n; } public String chooseNameSuffix(Element e) { if (e instanceof Struct) { Struct struct = (Struct) e; if (struct.getType() == Struct.Type.CStruct) { return "struct"; } else if (struct.getType() == Struct.Type.CUnion) { return "union"; } } else if (e instanceof Enum) { return "enum"; } else if (e instanceof FunctionSignature) { return "callback"; } return null; } static <T extends Element> T importDetails(T t, Element e, boolean move) { t.importDetails(e, move); return t; } }