All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bytedeco.javacpp.tools.Parser Maven / Gradle / Ivy

There is a newer version: 1.5.11
Show newest version
/*
 * Copyright (C) 2013-2016 Samuel Audet
 *
 * Licensed either under the Apache License, Version 2.0, or (at your option)
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation (subject to the "Classpath" exception),
 * either version 2, or any later version (collectively, the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     http://www.gnu.org/licenses/
 *     http://www.gnu.org/software/classpath/license.html
 *
 * or as provided in the LICENSE.txt file that accompanied this code.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bytedeco.javacpp.tools;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.bytedeco.javacpp.ClassProperties;
import org.bytedeco.javacpp.Loader;

/**
 * The Parser, just like the Generator, is a mess that is not meant to support the
 * entirety of C++, but an appropriate subset as used by typical C/C++ header files.
 * To figure out what that subset is and what the output should be, the idea is to
 * apply it on as many C/C++ libraries as possible, and patch the code as we go.
 * At one point in time, when this prototype code appears to have stabilized, we can
 * start to redesign it in a more meaningful way.
 * 

* That said, to understand how it is supposed to function in its present state, * one can step through the code at runtime: It is quite friendly to debuggers. *

* Moreover, it relies on {@link Info} objects created as part of the execution * of {@link InfoMapper#map(InfoMap)}. We can understand better how the parsing * is supposed to get accomplished by studying that documentation as well. *

* To do: * - Inherit constructors from helper classes, if possible * - etc. * * @see Info * @see InfoMap * @see InfoMapper * * @author Samuel Audet */ public class Parser { public Parser(Logger logger, Properties properties) { this(logger, properties, null); } public Parser(Logger logger, Properties properties, String lineSeparator) { this.logger = logger; this.properties = properties; this.lineSeparator = lineSeparator; } Parser(Parser p, String text) { this.logger = p.logger; this.properties = p.properties; this.infoMap = p.infoMap; this.tokens = new TokenIndexer(infoMap, new Tokenizer(text).tokenize(), false); this.lineSeparator = p.lineSeparator; } final Logger logger; final Properties properties; InfoMap infoMap = null; InfoMap leafInfoMap = null; TokenIndexer tokens = null; String lineSeparator = null; String translate(String text) { int namespace = text.lastIndexOf("::"); if (namespace >= 0) { Info info2 = infoMap.getFirst(text.substring(0, namespace)); text = text.substring(namespace + 2); if (info2 != null && info2.pointerTypes != null) { text = info2.pointerTypes[0] + "." + text; } } return text; } void containers(Context context, DeclarationList declList) throws ParserException { List basicContainers = new ArrayList(); for (Info info : infoMap.get("basic/containers")) { basicContainers.addAll(Arrays.asList(info.cppTypes)); } for (String containerName : basicContainers) { List infoList = leafInfoMap.get(containerName); for (Info info : infoList) { Declaration decl = new Declaration(); if (info == null || info.skip || !info.define) { continue; } int dim = containerName.startsWith("std::pair") ? 0 : 1; boolean resizable; Type containerType = new Parser(this, info.cppNames[0]).type(context), indexType, valueType, firstType = null, secondType = null; if (containerType.arguments == null || containerType.arguments.length == 0 || containerType.arguments[0] == null || containerType.arguments[containerType.arguments.length - 1] == null) { continue; } else if (containerType.arguments.length > 1 && containerType.arguments[1].javaName.length() > 0) { resizable = false; indexType = containerType.arguments[0]; valueType = containerType.arguments[1]; } else { resizable = containerType.arguments.length == 1; // assume second argument is the fixed size indexType = new Type(); indexType.annotations = "@Cast(\"size_t\") "; indexType.cppName = "size_t"; indexType.javaName = "long"; valueType = containerType.arguments[0]; } if (valueType.javaName == null || valueType.javaName.length() == 0 || containerName.startsWith("std::bitset")) { valueType.javaName = "boolean"; resizable = false; } while (valueType.cppName.startsWith(containerName) && leafInfoMap.get(valueType.cppName, false).size() == 0) { // increase dimension, unless the user has provided info for the intermediate type dim++; valueType = valueType.arguments[0]; } if (containerName.startsWith("std::pair")) { firstType = containerType.arguments[0]; secondType = containerType.arguments[1]; } if (valueType.cppName.startsWith("std::pair")) { firstType = valueType.arguments[0]; secondType = valueType.arguments[1]; } if (firstType != null && (firstType.annotations == null || firstType.annotations.length() == 0)) { firstType.annotations = (firstType.constValue ? "@Const " : "") + (firstType.indirections == 0 && !firstType.value ? "@ByRef " : ""); } if (secondType != null && (secondType.annotations == null || secondType.annotations.length() == 0)) { secondType.annotations = (secondType.constValue ? "@Const " : "") + (secondType.indirections == 0 && !secondType.value ? "@ByRef " : ""); } if (valueType != null && (valueType.annotations == null || valueType.annotations.length() == 0)) { valueType.annotations = (valueType.constValue ? "@Const " : "") + (valueType.indirections == 0 && !valueType.value ? "@ByRef " : ""); } Info valueInfo = infoMap.getFirst(valueType.cppName); if (valueInfo != null && valueInfo.cast) { String cast = valueType.cppName; if (valueType.constValue && !cast.startsWith("const ")) { cast = "const " + cast; } if (valueType.constPointer && !cast.endsWith(" const")) { cast = cast + " const"; } if (valueType.indirections > 0) { for (int i = 0; i < valueType.indirections; i++) { cast += "*"; } } if (valueType.reference) { cast += "&"; } valueType.annotations = "@Cast(\"" + cast + "\") " + valueType.annotations; } String arrayBrackets = ""; for (int i = 0; i < dim - 1; i++) { arrayBrackets += "[]"; } decl.text += (dim == 0 ? "\n@NoOffset " : "\n") + "@Name(\"" + containerType.cppName + "\") public static class " + containerType.javaName + " extends Pointer {\n" + " static { Loader.load(); }\n" + " /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */\n" + " public " + containerType.javaName + "(Pointer p) { super(p); }\n"; if ((dim == 0 || containerType.arguments.length == 1) && firstType != null && secondType != null) { String[] firstNames = firstType.javaNames != null ? firstType.javaNames : new String[] {firstType.javaName}; String[] secondNames = secondType.javaNames != null ? secondType.javaNames : new String[] {secondType.javaName}; String brackets = arrayBrackets + (dim > 0 ? "[]" : ""); for (int n = 0; n < firstNames.length || n < secondNames.length; n++) { decl.text += " public " + containerType.javaName + "(" + firstNames[Math.min(n, firstNames.length - 1)] + brackets + " firstValue, " + secondNames[Math.min(n, secondNames.length - 1)] + brackets + " secondValue) " + "{ this(" + (dim > 0 ? "Math.min(firstValue.length, secondValue.length)" : "") + "); put(firstValue, secondValue); }\n"; } } else if (resizable && firstType == null && secondType == null) { for (String javaName : valueType.javaNames != null ? valueType.javaNames : new String[] {valueType.javaName}) { decl.text += " public " + containerType.javaName + "(" + javaName + arrayBrackets + " ... array) { this(array.length); put(array); }\n"; } } decl.text += " public " + containerType.javaName + "() { allocate(); }\n" + (!resizable ? "" : " public " + containerType.javaName + "(long n) { allocate(n); }\n") + " private native void allocate();\n" + (!resizable ? "" : " private native void allocate(@Cast(\"size_t\") long n);\n") + " public native @Name(\"operator=\") @ByRef " + containerType.javaName + " put(@ByRef " + containerType.javaName + " x);\n\n"; for (int i = 0; i < dim; i++) { String indexAnnotation = i > 0 ? ("@Index" + (i > 1 ? "(" + i + ") " : " " )) : ""; String indices = "", separator = ""; for (int j = 0; j < i; j++) { indices += separator + indexType.annotations + indexType.javaName + " " + (char)('i' + j); separator = ", "; } decl.text += " public native " + indexAnnotation + "long size(" + indices + ");\n" + (!resizable ? "" : " public native " + indexAnnotation + "void resize(" + indices + separator + "@Cast(\"size_t\") long n);\n"); } String params = "", separator = ""; for (int i = 0; i < dim; i++) { params += separator + indexType.annotations + indexType.javaName + " " + (char)('i' + i); separator = ", "; } if (firstType != null && secondType != null) { String indexAnnotation = dim == 0 ? "@MemberGetter " : "@Index" + (dim > 1 ? "(" + dim + ") " : " "); decl.text += "\n" + " " + indexAnnotation + "public native " + firstType.annotations + firstType.javaName + " first(" + params + ");" + " public native " + containerType.javaName + " first(" + params + separator + firstType.javaName + " first);\n" + " " + indexAnnotation + "public native " + secondType.annotations + secondType.javaName + " second(" + params + "); " + " public native " + containerType.javaName + " second(" + params + separator + secondType.javaName + " second);\n"; for (int i = 1; firstType.javaNames != null && i < firstType.javaNames.length; i++) { decl.text += " @MemberSetter @Index public native " + containerType.javaName + " first(" + params + separator + firstType.annotations + firstType.javaNames[i] + " first);\n"; } for (int i = 1; secondType.javaNames != null && i < secondType.javaNames.length; i++) { decl.text += " @MemberSetter @Index public native " + containerType.javaName + " second(" + params + separator + secondType.annotations + secondType.javaNames[i] + " second);\n"; } } else { decl.text += "\n" + " @Index public native " + valueType.annotations + valueType.javaName + " get(" + params + ");\n" + " public native " + containerType.javaName + " put(" + params + separator + valueType.javaName + " value);\n"; for (int i = 1; valueType.javaNames != null && i < valueType.javaNames.length; i++) { decl.text += " @ValueSetter @Index public native " + containerType.javaName + " put(" + params + separator + valueType.annotations + valueType.javaNames[i] + " value);\n"; } if (containerType.arguments.length > 1 && containerType.arguments[1].javaName.length() > 0) { decl.text += "\n" + " public native @ByVal Iterator begin();\n" + " public native @ByVal Iterator end();\n" + " @NoOffset @Name(\"iterator\") public static class Iterator extends Pointer {\n" + " public Iterator(Pointer p) { super(p); }\n" + " public Iterator() { }\n\n" + " public native @Name(\"operator++\") @ByRef Iterator increment();\n" + " public native @Name(\"operator==\") boolean equals(@ByRef Iterator it);\n" + " public native @Name(\"operator*().first\") @MemberGetter " + indexType.annotations + indexType.javaName + " first();\n" + " public native @Name(\"operator*().second\") @MemberGetter " + valueType.annotations + valueType.javaName + " second();\n" // + " public native @Name(\"operator*\") " + valueType.annotations + valueType.javaName + " get();\n" + " }\n"; } } if ((dim == 0 || containerType.arguments.length == 1) && firstType != null && secondType != null) { String[] firstNames = firstType.javaNames != null ? firstType.javaNames : new String[] {firstType.javaName}; String[] secondNames = secondType.javaNames != null ? secondType.javaNames : new String[] {secondType.javaName}; String brackets = arrayBrackets + (dim > 0 ? "[]" : ""); for (int n = 0; n < firstNames.length || n < secondNames.length; n++) { decl.text += "\n" + " public " + containerType.javaName + " put(" + firstNames[Math.min(n, firstNames.length - 1)] + brackets + " firstValue, " + secondNames[Math.min(n, secondNames.length - 1)] + brackets + " secondValue) {\n"; String indent = " ", indices = "", args = ""; separator = ""; for (int i = 0; i < dim; i++) { char c = (char)('i' + i); decl.text += indent + "for (int " + c + " = 0; " + c + " < firstValue" + indices + ".length && " + c + " < secondValue" + indices + ".length; " + c + "++) {\n"; indent += " "; indices += "[" + c + "]"; args += separator + c; separator = ", "; } decl.text += indent + "first(" + args + separator + "firstValue" + indices + ");\n" + indent + "second(" + args + separator + "secondValue" + indices + ");\n"; for (int i = 0; i < dim; i++) { indent = indent.substring(4); decl.text += indent + "}\n"; } decl.text += " return this;\n" + " }\n"; } } else if (resizable && firstType == null && secondType == null) { for (String javaName : valueType.javaNames != null ? valueType.javaNames : new String[] {valueType.javaName}) { decl.text += "\n" + " public " + containerType.javaName + " put(" + javaName + arrayBrackets + " ... array) {\n"; String indent = " ", indices = "", args = ""; separator = ""; for (int i = 0; i < dim; i++) { char c = (char)('i' + i); decl.text += indent + "if (size(" + args + ") != array" + indices + ".length) { resize(" + args + separator + "array" + indices + ".length); }\n" + indent + "for (int " + c + " = 0; " + c + " < array" + indices + ".length; " + c + "++) {\n"; indent += " "; indices += "[" + c + "]"; args += separator + c; separator = ", "; } decl.text += indent + "put(" + args + separator + "array" + indices + ");\n"; for (int i = 0; i < dim; i++) { indent = indent.substring(4); decl.text += indent + "}\n"; } decl.text += " return this;\n" + " }\n"; } } decl.text += "}\n"; declList.add(decl); } } } TemplateMap template(Context context) throws ParserException { if (!tokens.get().match(Token.TEMPLATE)) { return null; } TemplateMap map = new TemplateMap(context.templateMap); tokens.next().expect('<'); for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { if (token.match(Token.IDENTIFIER)) { Token t = tokens.next(); if (t.match("...")) { map.variadic = true; t = tokens.next(); } if (t.match(Token.IDENTIFIER)) { String key = t.value; map.put(key, map.get(key)); token = tokens.next(); } } if (!token.match(',', '>')) { // ignore default argument int count = 0; for (token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (count == 0 && token.match(',', '>')) { break; } else if (token.match('<', '(')) { count++; } else if (token.match('>', ')')) { count--; } } } if (token.expect(',', '>').match('>')) { if (tokens.next().match(Token.TEMPLATE)) { tokens.next().expect('<'); } else { break; } } } return map; } Type[] templateArguments(Context context) throws ParserException { if (!tokens.get().match('<')) { return null; } List arguments = new ArrayList(); for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { Type type = type(context); arguments.add(type); token = tokens.get(); if (!token.match(',', '>')) { // may not actually be a type int count = 0; for (token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (count == 0 && token.match(',', '>')) { break; } else if (token.match('<', '(')) { count++; } else if (token.match('>', ')')) { count--; } type.cppName += token; if (token.match(Token.CONST)) { type.cppName += " "; } } if (type.cppName.endsWith("*")) { type.javaName = "PointerPointer"; type.annotations += "@Cast(\"" + type.cppName + "*\") "; } } if (token.expect(',', '>').match('>')) { break; } } return arguments.toArray(new Type[arguments.size()]); } Type type(Context context) throws ParserException { Type type = new Type(); List attributes = new ArrayList(); for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.get()) { if (token.match("::")) { type.cppName += token; } else if (token.match(Token.DECLTYPE)) { type.cppName += token.toString() + tokens.next().expect('('); int count = 0; for (token = tokens.next(); !token.match(')', Token.EOF); token = tokens.next()) { type.cppName += token; } type.cppName += token; tokens.next(); break; } else if (token.match('<')) { type.arguments = templateArguments(context); type.cppName += "<"; String separator = ""; for (Type t : type.arguments) { if (t == null) { // skip over variadic templates continue; } type.cppName += separator; Info info = infoMap.getFirst(t.cppName); String s = info != null && info.cppTypes != null ? info.cppTypes[0] : t.cppName; if (t.constValue && !s.startsWith("const ")) { s = "const " + s; } if (t.constPointer && !s.endsWith(" const")) { s = s + " const"; } for (int i = 0; i < t.indirections; i++) { s += "*"; } if (t.reference) { s += "&"; } type.cppName += s; separator = ","; } type.cppName += type.cppName.endsWith(">") ? " >" : ">"; } else if (token.match(Token.CONST, Token.CONSTEXPR)) { if (type.cppName.length() == 0) { type.constValue = true; } else { type.constPointer = true; } } else if (token.match('*')) { type.indirections++; tokens.next(); break; } else if (token.match('&')) { type.reference = true; tokens.next(); break; } else if (token.match("&&")) { // rvalue reference... ignore? } else if (token.match('~')) { type.cppName += "~"; type.destructor = true; } else if (token.match(Token.STATIC)) { type.staticMember = true; } else if (token.match(Token.OPERATOR)) { if (type.cppName.length() == 0) { type.operator = true; tokens.next(); continue; } else if (type.cppName.endsWith("::")) { type.operator = true; tokens.next(); break; } else { break; } } else if (token.match(Token.FRIEND)) { type.friend = true; } else if (token.match(Token.VIRTUAL)) { type.virtual = true; } else if (token.match(Token.AUTO, Token.ENUM, Token.EXPLICIT, Token.EXTERN, Token.INLINE, Token.CLASS, Token.INTERFACE, Token.__INTERFACE, Token.MUTABLE, Token.NAMESPACE, Token.STRUCT, Token.UNION, Token.TYPEDEF, Token.TYPENAME, Token.USING, Token.REGISTER, Token.THREAD_LOCAL, Token.VOLATILE)) { token = tokens.next(); continue; } else if (token.match((Object[])infoMap.getFirst("basic/types").cppTypes) && (type.cppName.length() == 0 || type.simple)) { type.cppName += token.value + " "; type.simple = true; } else if (token.match(Token.IDENTIFIER)) { int backIndex = tokens.index; Attribute attr = attribute(); if (attr != null && attr.annotation) { type.annotations += attr.javaName; attributes.add(attr); continue; } else { tokens.index = backIndex; if (type.cppName.length() == 0 || type.cppName.endsWith("::") || type.cppName.endsWith("~")) { type.cppName += token.value; } else { Info info = infoMap.getFirst(tokens.get(1).value); if ((info != null && info.annotations != null) || !tokens.get(1).match('*', '&', Token.IDENTIFIER, Token.CONST, Token.CONSTEXPR)) { // we probably reached a variable or function name identifier break; } } } } else { if (token.match('}')) { type.anonymous = true; tokens.next(); } break; } tokens.next(); } if (attributes.size() > 0) { type.attributes = attributes.toArray(new Attribute[attributes.size()]); } type.cppName = type.cppName.trim(); if (tokens.get().match("...")) { // skip over variable arguments tokens.next(); if (tokens.get().match(Token.IDENTIFIER)) { // skip over template parameter packs as well tokens.next(); } return null; } else if (type.operator) { for (Token token = tokens.get(); !token.match(Token.EOF, '('); token = tokens.next()) { type.cppName += token; } } // remove * and & to query template map if (type.cppName.endsWith("*")) { type.indirections++; type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } if (type.cppName.endsWith("&")) { type.reference = true; type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } // perform template substitution if (context.templateMap != null) { String[] types = type.cppName.split("::"); String separator = ""; type.cppName = ""; List arguments = new ArrayList(); for (String t : types) { Type t2 = context.templateMap.get(t); type.cppName += separator + (t2 != null ? t2.cppName : t); if (t2 != null && t2.arguments != null) { arguments.addAll(Arrays.asList(t2.arguments)); } separator = "::"; } if (arguments.size() > 0) { type.arguments = arguments.toArray(new Type[arguments.size()]); } } // remove const, * and & after template substitution for consistency if (type.cppName.startsWith("const ")) { type.constValue = true; type.cppName = type.cppName.substring(6); } if (type.cppName.endsWith("*")) { type.indirections++; if (type.reference) { type.constValue = false; } type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } if (type.cppName.endsWith("&")) { type.reference = true; type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } if (type.cppName.endsWith(" const")) { type.constPointer = true; type.cppName = type.cppName.substring(0, type.cppName.length() - 6); } // guess the fully qualified C++ type with what's available in the InfoMap Info info = null; for (String name : context.qualify(type.cppName)) { if ((info = infoMap.getFirst(name, false)) != null) { type.cppName = name; break; } else if (infoMap.getFirst(name) != null) { type.cppName = name; } } if (info != null && info.cppTypes != null && info.cppTypes.length > 0) { // use user defined type type.cppName = info.cppTypes[0]; } // remove const, * and & after user defined substitution for consistency if (type.cppName.startsWith("const ")) { type.constValue = true; type.cppName = type.cppName.substring(6); } if (type.cppName.endsWith("*")) { type.indirections++; if (type.reference) { type.constValue = false; } type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } if (type.cppName.endsWith("&")) { type.reference = true; type.cppName = type.cppName.substring(0, type.cppName.length() - 1); } if (type.cppName.endsWith(" const")) { type.constPointer = true; type.cppName = type.cppName.substring(0, type.cppName.length() - 6); } // produce some appropriate name for the peer Java class, relying on Info if available int namespace = type.cppName.lastIndexOf("::"); int template = type.cppName.lastIndexOf('<'); type.javaName = namespace >= 0 && template < 0 ? type.cppName.substring(namespace + 2) : type.cppName; if (info != null) { if (type.indirections == 0 && !type.reference && info.valueTypes != null && info.valueTypes.length > 0) { type.javaName = info.valueTypes[0]; type.javaNames = info.valueTypes; type.value = true; } else if (info.pointerTypes != null && info.pointerTypes.length > 0) { type.javaName = info.pointerTypes[0]; type.javaNames = info.pointerTypes; } } if (type.operator) { if (type.constValue) { type.annotations += "@Const "; } if (type.indirections == 0 && !type.reference && !type.value) { type.annotations += "@ByVal "; } else if (type.indirections == 0 && type.reference && !type.value) { type.annotations += "@ByRef "; } type.annotations += "@Name(\"operator " + (type.constValue ? "const " : "") + type.cppName + (type.indirections > 0 ? "*" : type.reference ? "&" : "") + "\") "; } if (info != null && info.annotations != null) { for (String s : info.annotations) { type.annotations += s + " "; } } if (context.cppName != null && type.javaName.length() > 0) { String groupName = context.cppName; int template2 = groupName != null ? groupName.lastIndexOf('<') : -1; if (template < 0 && template2 >= 0) { groupName = groupName.substring(0, template2); } int namespace2 = groupName != null ? groupName.lastIndexOf("::") : -1; if (namespace < 0 && namespace2 >= 0) { groupName = groupName.substring(namespace2 + 2); } if (type.cppName.equals(groupName)) { type.constructor = !type.destructor && !type.operator && type.indirections == 0 && !type.reference && tokens.get().match('(', ':'); } type.javaName = context.shorten(type.javaName); } return type; } Declarator declarator(Context context, String defaultName, int infoNumber, boolean useDefaults, int varNumber, boolean arrayAsPointer, boolean pointerAsArray) throws ParserException { boolean typedef = tokens.get().match(Token.TYPEDEF); boolean using = tokens.get().match(Token.USING); Declarator dcl = new Declarator(); Type type = type(context); if (type == null) { return null; } // pick the requested identifier out of the statement in the case of multiple variable declaractions int count = 0, number = 0; for (Token token = tokens.get(); number < varNumber && !token.match(Token.EOF); token = tokens.next()) { if (token.match('(','[','{')) { count++; } else if (token.match(')',']','}')) { count--; } else if (count > 0) { continue; } else if (token.match(',')) { number++; } else if (token.match(';')) { tokens.next(); return null; } } // start building an appropriate cast for the C++ type String precast = null, cast = type.cppName; if (type.constPointer) { dcl.constPointer = true; cast += " const"; } if (varNumber == 0 && type.indirections > 0) { dcl.indirections += type.indirections; for (int i = 0; i < type.indirections; i++) { cast += "*"; } } if (varNumber == 0 && type.reference) { dcl.reference = true; cast += "&"; } for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (token.match('*')) { dcl.indirections++; } else if (token.match('&')) { dcl.reference = true; } else if (token.match("&&")) { // rvalue reference... ignore? } else if (token.match(Token.CONST, Token.CONSTEXPR)) { dcl.constPointer = true; } else { break; } cast += token; } // translate C++ attributes to equivalent Java annotations List attributes = new ArrayList(); if (type.attributes != null) { attributes.addAll(Arrays.asList(type.attributes)); } int backIndex = tokens.index; Attribute attr = attribute(); while (attr != null && attr.annotation) { type.annotations += attr.javaName; attributes.add(attr); backIndex = tokens.index; attr = attribute(); } // consider attributes of the form SOMETHING(name) as hints for an appropriate Java name attr = null; tokens.index = backIndex; for (Attribute a : attributes) { if (a.arguments.length() > 0 && Character.isJavaIdentifierStart(a.arguments.charAt(0))) { attr = a; for (char c : a.arguments.toCharArray()) { if (!Character.isJavaIdentifierPart(c)) { attr = null; break; } } } if (attr != null) { break; } } // ignore superfluous parentheses count = 0; while (tokens.get().match('(') && tokens.get(1).match('(')) { tokens.next(); count++; } int[] dims = new int[256]; int indirections2 = 0; dcl.cppName = ""; Info groupInfo = null; Declaration definition = new Declaration(); boolean fieldPointer = false; if (tokens.get().match('(') || (typedef && tokens.get(1).match('('))) { // probably a function pointer declaration if (tokens.get().match('(')) { tokens.next(); } for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (token.match(Token.IDENTIFIER, "::")) { dcl.cppName += token; } else if (token.match('*')) { indirections2++; if (dcl.cppName.endsWith("::")) { dcl.cppName = dcl.cppName.substring(0, dcl.cppName.length() - 2); for (String name : context.qualify(dcl.cppName)) { if ((groupInfo = infoMap.getFirst(name, false)) != null) { dcl.cppName = name; break; } else if (infoMap.getFirst(name) != null) { dcl.cppName = name; } } definition.text += "@Namespace(\"" + dcl.cppName + "\") "; } else if (dcl.cppName.length() > 0) { definition.text += "@Convention(\"" + dcl.cppName + "\") "; } dcl.cppName = ""; } else if (token.match('[')) { Token n = tokens.get(1); dims[dcl.indices++] = n.match(Token.INTEGER) ? Integer.parseInt(n.value) : -1; } else if (token.match('(', ')')) { break; } } if (tokens.get().match(')')) { tokens.next(); } } else if (tokens.get().match(Token.IDENTIFIER)) { for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (dcl.cppName.length() > 0 && token.match('*')) { // a data member pointer or something dcl.cppName = dcl.cppName.substring(0, dcl.cppName.length() - 2); for (String name : context.qualify(dcl.cppName)) { if ((groupInfo = infoMap.getFirst(name, false)) != null) { dcl.cppName = name; break; } else if (infoMap.getFirst(name) != null) { dcl.cppName = name; } } definition.text += "@Namespace(\"" + dcl.cppName + "\") "; for (token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (token.match('*')) { indirections2++; } else { break; } } dcl.cppName = token.match(Token.IDENTIFIER) ? token.toString() : ""; fieldPointer = groupInfo != null; } else if (token.match("::")) { dcl.cppName += token; } else if (token.match(Token.OPERATOR)) { dcl.operator = true; if (!tokens.get(1).match(Token.IDENTIFIER) || tokens.get(1).match(Token.NEW, Token.DELETE)) { // assume we can have any symbols until the first open parenthesis dcl.cppName += "operator " + tokens.next(); for (token = tokens.next(); !token.match(Token.EOF, '('); token = tokens.next()) { dcl.cppName += token; } break; } } else if (token.match('<')) { // template arguments dcl.cppName += token; int count2 = 0; for (token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { dcl.cppName += token; if (count2 == 0 && token.match('>')) { break; } else if (token.match('<')) { count2++; } else if (token.match('>')) { count2--; } } } else if (token.match(Token.IDENTIFIER) && (dcl.cppName.length() == 0 || dcl.cppName.endsWith("::"))) { dcl.cppName += token; } else { break; } } } if (dcl.cppName.length() == 0) { dcl.cppName = defaultName; } boolean bracket = false; for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (!bracket && token.match('[')) { bracket = true; Token n = tokens.get(1); dims[dcl.indices++] = n.match(Token.INTEGER) ? Integer.parseInt(n.value) : -1; } else if (!bracket) { break; } else if (bracket && token.match(']')) { bracket = false; } } while (dcl.indices > 0 && indirections2 > 0) { // treat complex combinations of arrays and pointers as multidimensional arrays dims[dcl.indices++] = -1; indirections2--; } if (arrayAsPointer && dcl.indices > 0) { // treat array as an additional indirection dcl.indirections++; String dimCast = ""; for (int i = 1; i < dcl.indices; i++) { if (dims[i] > 0) { dimCast += "[" + dims[i] + "]"; } } if (!dimCast.isEmpty()) { if (dims[0] != -1) { // Annotate with the first dimension's value cast += "(* /*[" + dims[0] + "]*/ )"; } else { // Unknown size cast += "(*)"; } cast += dimCast; } else { cast += "*"; } } if (pointerAsArray && dcl.indirections > (type.anonymous ? 0 : 1)) { // treat second indirection as an array, unless anonymous dims[dcl.indices++] = -1; dcl.indirections--; cast = cast.substring(0, cast.length() - 1); } if (tokens.get().match(':')) { // ignore bitfields type.annotations += "@NoOffset "; tokens.next().expect(Token.INTEGER, Token.IDENTIFIER); tokens.next().expect(',', ';'); } // if this is a function pointer, get parameters dcl.parameters = parameters(context, infoNumber, useDefaults); int infoLength = 1; boolean valueType = false, needCast = arrayAsPointer && dcl.indices > 1, implicitConst = false; String prefix = type.constValue && dcl.indirections < 2 && !dcl.reference ? "const " : ""; Info info = infoMap.getFirst(prefix + type.cppName, false); if ((!typedef || dcl.parameters != null) && (info == null || (info.cppTypes != null && info.cppTypes.length > 0))) { // substitute template types that have no info with appropriate adapter annotation Type type2 = type; if (info != null) { type2 = new Parser(this, info.cppTypes[0]).type(context); } List infoList = infoMap.get(type2.cppName); for (Info info2 : infoList) { if (type2.arguments != null && info2.annotations != null) { type.constPointer = type2.arguments[0].constPointer; type.constValue = type2.arguments[0].constValue; type.simple = type2.arguments[0].simple; type.indirections = type2.arguments[0].indirections; type.reference = type2.arguments[0].reference; type.annotations = type2.arguments[0].annotations; type.cppName = type2.arguments[0].cppName; type.javaName = type2.arguments[0].javaName; dcl.indirections = 1; dcl.reference = false; if (context.virtualize) { // force cast in callbacks needCast = true; precast = cast; } cast = type.cppName + "*"; if (type.constValue && !cast.startsWith("const ")) { cast = "const " + cast; } if (type.constPointer && !cast.endsWith(" const")) { cast = cast + " const"; } if (type.indirections > 0) { dcl.indirections += type.indirections; for (int i = 0; i < type.indirections; i++) { cast += "*"; } } if (type.reference) { dcl.reference = true; cast += "&"; } for (String s : info2.annotations) { type.annotations += s + " "; } info = infoMap.getFirst(type.cppName, false); break; } } } if (!using && info != null) { valueType = info.valueTypes != null && ((type.constValue && dcl.reference) || (dcl.indirections == 0 && !dcl.reference) || info.pointerTypes == null); implicitConst = info.cppNames[0].startsWith("const "); infoLength = valueType ? info.valueTypes.length : info.pointerTypes != null ? info.pointerTypes.length : 1; dcl.infoNumber = infoNumber < 0 ? 0 : infoNumber % infoLength; type.javaName = valueType ? info.valueTypes[dcl.infoNumber] : info.pointerTypes != null ? info.pointerTypes[dcl.infoNumber] : type.javaName; type.javaName = context.shorten(type.javaName); needCast |= info.cast && !type.cppName.equals(type.javaName); } if (!valueType) { if (dcl.indirections == 0 && !dcl.reference) { type.annotations += "@ByVal "; } else if (dcl.indirections == 0 && dcl.reference) { if (type.javaName.contains("@ByPtrPtr ")) { type.javaName = type.javaName.replace("@ByPtrPtr ", "@ByPtrRef "); } else { type.annotations += "@ByRef "; } } else if (dcl.indirections == 1 && dcl.reference) { type.annotations += "@ByPtrRef "; } else if (dcl.indirections == 2 && !dcl.reference && infoNumber >= 0) { type.annotations += "@ByPtrPtr "; needCast |= type.cppName.equals("void"); } else if (dcl.indirections >= 2) { dcl.infoNumber += infoLength; needCast = true; type.javaName = "PointerPointer"; if (dcl.reference) { type.annotations += "@ByRef "; } } if (!needCast && !type.javaName.contains("@Cast")) { if (type.constValue && !implicitConst && !type.constPointer) { type.annotations = "@Const " + type.annotations; } else if (type.constPointer) { type.annotations = "@Const({" + type.constValue + ", " + type.constPointer + "}) " + type.annotations; } } } if (needCast) { if (dcl.indirections == 0 && dcl.reference) { // consider as pointer type cast = cast.replace('&', '*'); } if (valueType && type.constValue && dcl.reference) { // consider as value type cast = cast.substring(0, cast.length() - 1); } if (type.constValue && !cast.startsWith("const ")) { cast = "const " + cast; } if (precast != null) { type.annotations = "@Cast({\"" + cast + "\", \"" + precast + "\"}) " + type.annotations; } else if (!valueType && dcl.indirections == 0 && !dcl.reference) { type.annotations += "@Cast(\"" + cast + "*\") "; } else { type.annotations = "@Cast(\"" + cast + "\") " + type.annotations; } } // initialize shorten Java name and get fully qualitifed C++ name dcl.javaName = attr != null ? attr.arguments : dcl.cppName; for (String name : context.qualify(dcl.cppName)) { if ((info = infoMap.getFirst(name, false)) != null) { dcl.cppName = name; break; } else if (infoMap.getFirst(name) != null) { dcl.cppName = name; } } // pick the Java name from the InfoMap if appropriate String originalName = fieldPointer ? groupInfo.pointerTypes[0] : dcl.javaName; if (attr == null && defaultName == null && info != null && info.javaNames != null && info.javaNames.length > 0 && (dcl.operator || !info.cppNames[0].contains("<") || (context.templateMap != null && context.templateMap.type == null))) { dcl.javaName = info.javaNames[0]; } if (info != null && info.annotations != null) { for (String s : info.annotations) { type.annotations += s + " "; } } // deal with function parameters and function pointers dcl.type = type; dcl.signature = dcl.javaName; if (dcl.parameters != null || fieldPointer) { if (dcl.parameters != null) { dcl.infoNumber = Math.max(dcl.infoNumber, dcl.parameters.infoNumber); } if (dcl.parameters != null && indirections2 == 0 && !typedef) { dcl.signature += dcl.parameters.signature; } else { String cppType = ""; if (dcl.type != null) { cppType += dcl.type.cppName; for (int i = 0; i < dcl.indirections; i++) { cppType += "*"; } if (dcl.reference) { cppType += "&"; } } cppType += " (*)("; String separator = ""; if (dcl.parameters != null) { for (Declarator d : dcl.parameters.declarators) { if (d != null) { cppType += separator + d.type.cppName; for (int i = 0; i < d.indirections; i++) { cppType += "*"; } if (d.reference) { cppType += "&"; } separator = ", "; } } } info = infoMap.getFirst(cppType += ")"); String functionType = null; if (originalName != null) { functionType = Character.toUpperCase(originalName.charAt(0)) + originalName.substring(1); } if (info != null && info.pointerTypes != null && info.pointerTypes.length > 0) { functionType = info.pointerTypes[0]; } else if (typedef) { functionType = originalName; } else if (dcl.parameters != null && dcl.parameters.signature.length() > 0) { functionType += dcl.parameters.signature; } else if (!type.javaName.equals("void")) { functionType = Character.toUpperCase(type.javaName.charAt(0)) + type.javaName.substring(1) + "_" + functionType; } if (info != null && info.annotations != null) { for (String s : info.annotations) { definition.text += s + " "; } } definition.text += (tokens.get().match(Token.CONST, Token.CONSTEXPR) ? "@Const " : "") + "public static class " + functionType + " extends FunctionPointer {\n" + " static { Loader.load(); }\n" + " /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */\n" + " public " + functionType + "(Pointer p) { super(p); }\n" + (groupInfo != null ? "" : " protected " + functionType + "() { allocate(); }\n" + " private native void allocate();\n"); if (fieldPointer) { definition.text += " public native " + type.annotations + type.javaName + " get(" + groupInfo.pointerTypes[0] + " o);\n" + " public native " + functionType + " put(" + groupInfo.pointerTypes[0] + " o, " + type.annotations + type.javaName + " v);\n" + "}\n"; } else { definition.text += " public native " + type.annotations + type.javaName + " call" + (groupInfo != null ? "(" + groupInfo.pointerTypes[0] + " o" + (dcl.parameters.list.charAt(1) == ')' ? ")" : ", " + dcl.parameters.list.substring(1)) : dcl.parameters.list) + ";\n" + "}\n"; } definition.signature = functionType; definition.declarator = new Declarator(); definition.declarator.parameters = dcl.parameters; dcl.definition = definition; dcl.indirections = indirections2; if (!fieldPointer) { dcl.parameters = null; } type.annotations = ""; type.javaName = functionType; } } // annotate with @Name if the Java name doesn't match with the C++ name if (dcl.cppName != null) { String localName = dcl.cppName; if (context.namespace != null && localName.startsWith(context.namespace + "::")) { localName = dcl.cppName.substring(context.namespace.length() + 2); } int template = localName.lastIndexOf('<'); String simpleName = template >= 0 ? localName.substring(0, template) : localName; if (!simpleName.equals(dcl.javaName) && (!localName.contains("::") || context.javaName == null)) { type.annotations += "@Name(\"" + localName + "\") "; } } // ignore superfluous parentheses while (tokens.get().match(')') && count > 0) { tokens.next(); count--; } return dcl; } /** Documentation tags, where we keep only the ones that could be compatible between Javadoc and Doxygen. */ String[] docTags = {"author", "deprecated", "exception", "param", "return", "see", "since", "throws", "version", /* "code", "docRoot", "inheritDoc", "link", "linkplain", "literal", "serial", "serialData", "serialField", "value" */}; /** Tries to adapt a Doxygen-style documentation comment to Javadoc-style. */ String commentDoc(String s, int startIndex) { if (startIndex < 0 || startIndex > s.length()) { return s; } int index = s.indexOf("/**", startIndex); StringBuilder sb = new StringBuilder(s); while (index < sb.length()) { char c = sb.charAt(index); String ss = sb.substring(index + 1); if (c == '`' && ss.startsWith("``") && sb.length() - index > 3) { sb.replace(index, index + 3, "

{@code"
                        + (Character.isWhitespace(sb.charAt(index + 3)) ? "" : " "));
                index = sb.indexOf("```", index);
                if (index < 0) {
                    break;
                }
                sb.replace(index, index + 3, "}
"); } else if (c == '`') { sb.replace(index, index + 1, "{@code "); index = sb.indexOf("`", index); if (index < 0) { break; } sb.replace(index, index + 1, "}"); } else if ((c == '\\' || c == '@') && ss.startsWith("code")) { sb.replace(index, index + 5, "
{@code"
                        + (Character.isWhitespace(sb.charAt(index + 5)) ? "" : " "));
                index = sb.indexOf(c + "endcode", index);
                if (index < 0) {
                    break;
                }
                sb.replace(index, index + 8, "}
"); } else if ((c == '\\' || c == '@') && ss.startsWith("verbatim")) { sb.replace(index, index + 9, "
{@literal"
                        + (Character.isWhitespace(sb.charAt(index + 9)) ? "" : " "));
                index = sb.indexOf(c + "endverbatim", index);
                if (index < 0) {
                    break;
                }
                sb.replace(index, index + 12, "}
"); } else if (c == '\n' && ss.length() > 0 && ss.charAt(0) == '\n') { int n = 0; while (n < ss.length() && ss.charAt(n) == '\n') { n++; } String indent = ""; while (n < ss.length() && Character.isWhitespace(ss.charAt(n))) { indent += ss.charAt(n); n++; } sb.insert(index + 1, indent + "

"); } else if (c == '\\' || c == '@') { String foundTag = null; for (String tag : docTags) { if (ss.startsWith(tag)) { foundTag = tag; break; } } if (foundTag != null) { sb.setCharAt(index, '@'); int n = index + foundTag.length() + 1; if (sb.charAt(n) == 's' && !foundTag.endsWith("s")) { sb.deleteCharAt(n); } else if (!Character.isWhitespace(sb.charAt(n))) { sb.insert(n, ' '); } } else { // keep unmapped tags around as part of the comments sb.setCharAt(index, '\\'); } } else if (c == '*' && ss.charAt(0) == '/') { index = sb.indexOf("/**", index); if (index < 0) { break; } } index++; } return sb.toString(); } /** Converts Doxygen-like documentation comments placed before identifiers to Javadoc-style. * Also leaves as is non-documentation comments. */ String commentBefore() throws ParserException { String comment = ""; tokens.raw = true; while (tokens.index > 0 && tokens.get(-1).match(Token.COMMENT)) { tokens.index--; } boolean closeComment = false; int startDoc = -1; for (Token token = tokens.get(); token.match(Token.COMMENT); token = tokens.next()) { String s = token.value; if (s.startsWith("/**") || s.startsWith("/*!") || s.startsWith("///") || s.startsWith("//!")) { if (s.length() > 3 && s.charAt(3) == '<') { continue; } else if (s.length() >= 3 && (s.startsWith("///") || s.startsWith("//!")) && !s.startsWith("////") && !s.startsWith("///*")) { String lastComment = comment.trim(); int n2 = lastComment.indexOf('\n'); while (!lastComment.startsWith("/*") && n2 > 0) { lastComment = n2 + 1 < lastComment.length() ? lastComment.substring(n2 + 1).trim() : ""; n2 = lastComment.indexOf('\n'); } s = (comment.length() == 0 || comment.contains("*/") || !lastComment.startsWith("/*") ? "/**" : " * ") + s.substring(3); closeComment = true; } else if (s.length() > 3 && !s.startsWith("///")) { s = "/**" + s.substring(3); } } else if (closeComment && !comment.endsWith("*/")) { closeComment = false; comment += " */"; } if (startDoc < 0 && s.startsWith("/**")) { startDoc = comment.length(); } comment += token.spacing + s; } if (closeComment && !comment.endsWith("*/")) { comment += " */"; } tokens.raw = false; return commentDoc(comment, startDoc); } /** Converts Doxygen-like documentation comments placed after identifiers to Javadoc-style. */ String commentAfter() throws ParserException { String comment = ""; tokens.raw = true; while (tokens.index > 0 && tokens.get(-1).match(Token.COMMENT)) { tokens.index--; } boolean closeComment = false; int startDoc = -1; for (Token token = tokens.get(); token.match(Token.COMMENT); token = tokens.next()) { String s = token.value; String spacing = token.spacing; int n = spacing.lastIndexOf('\n') + 1; if (s.startsWith("/**") || s.startsWith("/*!") || s.startsWith("///") || s.startsWith("//!")) { if (s.length() > 3 && s.charAt(3) != '<') { continue; } else if (s.length() > 4 && (s.startsWith("///") || s.startsWith("//!"))) { String lastComment = comment.trim(); int n2 = lastComment.indexOf('\n'); while (!lastComment.startsWith("/*") && n2 > 0) { lastComment = n2 + 1 < lastComment.length() ? lastComment.substring(n2 + 1).trim() : ""; n2 = lastComment.indexOf('\n'); } s = (comment.length() == 0 || comment.contains("*/") || !lastComment.startsWith("/*") ? "/**" : " * ") + s.substring(4); closeComment = true; } else if (s.length() > 4) { s = "/**" + s.substring(4); } if (startDoc < 0 && s.startsWith("/**")) { startDoc = comment.length(); } comment += spacing.substring(0, n) + s; } } if (closeComment && !comment.endsWith("*/")) { comment += " */"; } if (comment.length() > 0) { comment += "\n"; } tokens.raw = false; return commentDoc(comment, startDoc); } Attribute attribute() throws ParserException { if (!tokens.get().match(Token.IDENTIFIER)) { return null; } Attribute attr = new Attribute(); Info info = infoMap.getFirst(attr.cppName = tokens.get().value); if (attr.annotation = info != null && info.annotations != null && info.javaNames == null && info.valueTypes == null && info.pointerTypes == null) { for (String s : info.annotations) { attr.javaName += s + " "; } } if (!tokens.next().match('(')) { return attr; } int count = 1; tokens.raw = true; for (Token token = tokens.next(); !token.match(Token.EOF) && count > 0; token = tokens.next()) { if (token.match('(')) { count++; } else if (token.match(')')) { count--; } else if (info == null || !info.skip) { attr.arguments += token.value; } } tokens.raw = false; return attr; } String body() throws ParserException { if (!tokens.get().match('{')) { return null; } int count = 1; tokens.raw = true; for (Token token = tokens.next(); !token.match(Token.EOF) && count > 0; token = tokens.next()) { if (token.match('{')) { count++; } else if (token.match('}')) { count--; } } tokens.raw = false; return ""; } Parameters parameters(Context context, int infoNumber, boolean useDefaults) throws ParserException { int backIndex = tokens.index; if (!tokens.get().match('(')) { return null; } int count = 0; Parameters params = new Parameters(); List dcls = new ArrayList(); params.list = "("; params.names = "("; int lastVarargs = -1; for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.get()) { String spacing = token.spacing; if (token.match(')')) { params.list += spacing + ")"; params.names += ")"; tokens.next(); break; } Declarator dcl = declarator(context, "arg" + count++, infoNumber, useDefaults, 0, true, false); boolean hasDefault = !tokens.get().match(',', ')'); Token defaultToken = null; String defaultValue = ""; if (dcl != null && hasDefault) { defaultToken = tokens.get(); int count2 = 0; for (token = tokens.next(), token.spacing = ""; !token.match(Token.EOF); token = tokens.next()) { if (count2 == 0 && token.match(',', ')')) { break; } else if (token.match('(')) { count2++; } else if (token.match(')')) { count2--; } // try to qualify all the identifiers String cppName = token.value; for (String name : context.qualify(cppName)) { if (infoMap.getFirst(name, false) != null) { cppName = name; break; } else if (infoMap.getFirst(name) != null) { cppName = name; } } if (token.match(Token.IDENTIFIER)) { while (tokens.get(1).equals("::")) { tokens.next(); Token t = tokens.next(); cppName += "::" + t.spacing + t; } } defaultValue += token.spacing + (cppName != null && cppName.length() > 0 ? cppName : token); } for (String name : context.qualify(defaultValue)) { if (infoMap.getFirst(name, false) != null) { defaultValue = name; break; } else if (infoMap.getFirst(name) != null) { defaultValue = name; } } // insert default value as nullValue for pass by value or by reference String s = dcl.type.annotations; int n = s.indexOf("@ByVal "); if (n < 0) { n = s.indexOf("@ByRef "); } if (n >= 0) { if (!defaultValue.startsWith(dcl.type.cppName)) { defaultValue = dcl.type.cppName + "(" + defaultValue + ")"; } Info info = infoMap.getFirst(defaultValue); if (info != null && info.skip) { if (useDefaults) { tokens.index = backIndex; return parameters(context, infoNumber, false); } } else { defaultValue = defaultValue.replaceAll("\"", "\\\\\"").replaceAll("\n(\\s*)", "\"\n$1 + \""); s = s.substring(0, n + 6) + "(nullValue = \"" + defaultValue + "\")" + s.substring(n + 6); } } dcl.type.annotations = s; } if (dcl != null && !dcl.type.javaName.equals("void") && (!hasDefault || !useDefaults)) { if (lastVarargs >= 0) { // substitute varargs that are not last with array params.list = params.list.substring(0, lastVarargs) + "[]" + params.list.substring(lastVarargs + 3); } int n = params.list.length(); params.infoNumber = Math.max(params.infoNumber, dcl.infoNumber); params.list += (count > 1 ? "," : "") + spacing + dcl.type.annotations + dcl.type.javaName + " " + dcl.javaName; lastVarargs = params.list.indexOf("...", n); if (hasDefault && !dcl.type.annotations.contains("(nullValue = ")) { // output default argument as a comment params.list += "/*" + defaultToken + defaultValue + "*/"; } params.signature += '_'; for (char c : dcl.type.javaName.substring(dcl.type.javaName.lastIndexOf(' ') + 1).toCharArray()) { params.signature += Character.isJavaIdentifierPart(c) ? c : '_'; } params.names += (count > 1 ? ", " : "") + dcl.javaName; if (dcl.javaName.startsWith("arg")) { try { count = Integer.parseInt(dcl.javaName.substring(3)) + 1; } catch (NumberFormatException e) { /* don't care if not int */ } } } if (!hasDefault || !useDefaults) { dcls.add(dcl); } if (tokens.get().expect(',', ')').match(',')) { tokens.next(); } } params.declarators = dcls.toArray(new Declarator[dcls.size()]); return params; } boolean function(Context context, DeclarationList declList) throws ParserException { int backIndex = tokens.index; String spacing = tokens.get().spacing; String modifiers = "public native "; int startIndex = tokens.index; Type type = type(context); Parameters params = parameters(context, 0, false); Declarator dcl = new Declarator(); Declaration decl = new Declaration(); if (type.javaName.length() == 0) { // not a function, probably an attribute tokens.index = backIndex; return false; } else if (context.javaName == null && !type.operator && params != null) { // this is a constructor definition or specialization, skip over if (tokens.get().match(':')) { for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { if (token.match('{', ';')) { break; } } } if (tokens.get().match('{')) { body(); } else { tokens.next(); } decl.text = spacing; decl.function = true; declList.add(decl); return true; } else if ((type.constructor || type.destructor || type.operator) && params != null) { // this is a constructor, destructor, or cast operator dcl.type = type; dcl.parameters = params; dcl.cppName = type.cppName; dcl.javaName = type.javaName.substring(type.javaName.lastIndexOf(' ') + 1); if (type.operator) { dcl.cppName = "operator " + dcl.cppName; dcl.javaName = "as" + Character.toUpperCase(dcl.javaName.charAt(0)) + dcl.javaName.substring(1); } dcl.signature = dcl.javaName + params.signature; } else { tokens.index = startIndex; dcl = declarator(context, null, 0, false, 0, false, false); type = dcl.type; } if (dcl.cppName == null || type.javaName.length() == 0 || dcl.parameters == null) { tokens.index = backIndex; return false; } int namespace = dcl.cppName.lastIndexOf("::"); if (context.namespace != null && namespace < 0) { dcl.cppName = context.namespace + "::" + dcl.cppName; } Info info = null; String fullname = dcl.cppName, fullname2 = dcl.cppName; if (dcl.parameters != null) { fullname += "("; fullname2 += "("; String separator = ""; for (Declarator d : dcl.parameters.declarators) { if (d != null) { String s = d.type.cppName; String s2 = d.type.cppName; if (d.type.constValue && !s.startsWith("const ")) { s = "const " + s; } if (d.type.constPointer && !s.endsWith(" const")) { s = s + " const"; } if (d.indirections > 0) { for (int i = 0; i < d.indirections; i++) { s += "*"; s2 += "*"; } } if (d.reference) { s += "&"; s2 += "&"; } fullname += separator + s; fullname2 += separator + s2; separator = ", "; } } info = infoMap.getFirst(fullname += ")"); if (info == null) { info = infoMap.getFirst(fullname2 += ")"); } } if (info == null) { info = infoMap.getFirst(dcl.cppName); if (!type.constructor && !type.destructor && !type.operator) { infoMap.put(info != null ? new Info(info).cppNames(fullname) : new Info(fullname)); } } String localName = dcl.cppName; if (localName.startsWith(context.namespace + "::")) { localName = dcl.cppName.substring(context.namespace.length() + 2); } int localNamespace = 0; int templateCount = 0; for (int i = 0; i < localName.length(); i++) { int c = localName.charAt(i); if (c == '<') { templateCount++; } else if (c == '>') { templateCount--; } else if (templateCount == 0 && localName.substring(i).startsWith("::")) { localNamespace = i; break; } } if (type.friend || (context.javaName == null && localNamespace > 0) || (info != null && info.skip)) { // this is a friend declaration, or a member function definition or specialization, skip over for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.get()) { if (attribute() == null) { break; } } if (tokens.get().match(':')) { for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { if (token.match('{', ';')) { break; } } } if (tokens.get().match('{')) { body(); } else { tokens.next(); } decl.text = spacing; decl.function = true; declList.add(decl); return true; } else if (type.staticMember || context.javaName == null) { modifiers = "public static native "; if (tokens.isCFile) { modifiers = "@NoException " + modifiers; } } List prevDcl = new ArrayList(); boolean first = true; for (int n = -2; n < Integer.MAX_VALUE; n++) { decl = new Declaration(); tokens.index = startIndex; if ((type.constructor || type.destructor || type.operator) && params != null) { type = type(context); params = parameters(context, n / 2, n % 2 != 0); dcl = new Declarator(); dcl.type = type; dcl.parameters = params; dcl.cppName = type.cppName; dcl.javaName = type.javaName.substring(type.javaName.lastIndexOf(' ') + 1); if (type.operator) { dcl.cppName = "operator " + dcl.cppName; dcl.javaName = "as" + Character.toUpperCase(dcl.javaName.charAt(0)) + dcl.javaName.substring(1); } dcl.signature = dcl.javaName + params.signature; if (tokens.get().match(':')) { for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { if (token.match('{', ';')) { break; } } } } else { dcl = declarator(context, null, n / 2, n % 2 != 0, 0, false, false); type = dcl.type; namespace = dcl.cppName.lastIndexOf("::"); if (context.namespace != null && namespace < 0) { dcl.cppName = context.namespace + "::" + dcl.cppName; } } // check for const and pure virtual functions, ignoring the body if present for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.get()) { decl.constMember |= token.match(Token.CONST, Token.CONSTEXPR); if (attribute() == null) { break; } } if (tokens.get().match('{')) { body(); } else { // may be a pure virtual function if (tokens.get().match('=')) { Token token = tokens.next().expect("0", Token.DELETE, Token.DEFAULT); if (token.match("0")) { decl.abstractMember = true; } else if (token.match(Token.DELETE)) { decl.text = spacing; declList.add(decl); return true; } tokens.next().expect(';'); } tokens.next(); } // add @Virtual annotation on user request only, inherited through context if (type.virtual && context.virtualize) { modifiers = "@Virtual" + (decl.abstractMember ? "(true) " : " ") + (context.inaccessible ? "protected native " : "public native "); } // compose the text of the declaration with the info we got up until this point decl.declarator = dcl; if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } if (type.constructor && params != null) { decl.text += "public " + context.shorten(context.javaName) + dcl.parameters.list + " { super((Pointer)null); allocate" + params.names + "; }\n" + "private native " + type.annotations + "void allocate" + dcl.parameters.list + ";\n"; } else { decl.text += modifiers + type.annotations + context.shorten(type.javaName) + " " + dcl.javaName + dcl.parameters.list + ";\n"; } decl.signature = dcl.signature; // replace all of the declaration by user specified text if (info != null && info.javaText != null) { if (first) { decl.text = info.javaText; } else { break; } } String comment = commentAfter(); if (first) { declList.spacing = spacing; decl.text = comment + decl.text; } decl.function = true; // only add nonduplicate declarations and ignore destructors boolean found = false; for (Declarator d : prevDcl) { found |= dcl.signature.equals(d.signature); } if (dcl.javaName.length() > 0 && !found && (!type.destructor || (info != null && info.javaText != null))) { if (declList.add(decl)) { first = false; } if (type.virtual && context.virtualize) { break; } } else if (found && n / 2 > 0 && n % 2 == 0 && n / 2 > Math.max(dcl.infoNumber, dcl.parameters.infoNumber)) { break; } prevDcl.add(dcl); } declList.spacing = null; return true; } boolean variable(Context context, DeclarationList declList) throws ParserException { int backIndex = tokens.index; String spacing = tokens.get().spacing; String modifiers = "public static native "; String setterType = "void "; Declarator dcl = declarator(context, null, 0, false, 0, false, true); Declaration decl = new Declaration(); String cppName = dcl.cppName; String javaName = dcl.javaName; if (javaName == null || !tokens.get().match('[', '=', ',', ':', ';')) { tokens.index = backIndex; return false; } else if (!dcl.type.staticMember && context.javaName != null) { modifiers = "public native "; setterType = context.shorten(context.javaName) + " "; } int namespace = cppName.lastIndexOf("::"); if (context.namespace != null && namespace < 0) { cppName = context.namespace + "::" + cppName; } Info info = infoMap.getFirst(cppName); if (info != null && info.skip) { decl.text = spacing; declList.add(decl); return true; } else if (info == null) { Info info2 = infoMap.getFirst(dcl.cppName); infoMap.put(info2 != null ? new Info(info2).cppNames(cppName) : new Info(cppName)); } boolean first = true; Declarator metadcl = context.variable; for (int n = 0; n < Integer.MAX_VALUE; n++) { decl = new Declaration(); tokens.index = backIndex; dcl = declarator(context, null, -1, false, n, false, true); if (dcl == null || dcl.cppName == null) { break; } decl.declarator = dcl; cppName = dcl.cppName; namespace = cppName.lastIndexOf("::"); if (context.namespace != null && namespace < 0) { cppName = context.namespace + "::" + cppName; } info = infoMap.getFirst(cppName); namespace = cppName.lastIndexOf("::"); String shortName = cppName; if (namespace >= 0) { shortName = cppName.substring(namespace + 2); } javaName = dcl.javaName; if (metadcl == null || metadcl.indices == 0 || dcl.indices == 0) { // arrays are currently not supported for both metadcl and dcl at the same time String indices = ""; for (int i = 0; i < (metadcl == null || metadcl.indices == 0 ? dcl.indices : metadcl.indices); i++) { if (i > 0) { indices += ", "; } indices += "int " + (char)('i' + i); } if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } if (metadcl != null && metadcl.cppName.length() > 0) { decl.text += metadcl.indices == 0 ? "@Name(\"" + metadcl.cppName + "." + shortName + "\") " : "@Name({\"" + metadcl.cppName + "\", \"." + shortName + "\"}) "; dcl.type.annotations = dcl.type.annotations.replaceAll("@Name\\(.*\\) ", ""); javaName = metadcl.javaName + "_" + shortName; } if (dcl.type.constValue) { decl.text += "@MemberGetter "; } decl.text += modifiers + dcl.type.annotations.replace("@ByVal ", "@ByRef ") + dcl.type.javaName + " " + javaName + "(" + indices + ");"; if (!dcl.type.constValue) { if (indices.length() > 0) { indices += ", "; } decl.text += " " + modifiers + setterType + javaName + "(" + indices + dcl.type.javaName + " " + javaName + ");"; } decl.text += "\n"; if (dcl.type.constValue && dcl.type.staticMember && indices.length() == 0) { String rawType = dcl.type.javaName.substring(dcl.type.javaName.lastIndexOf(' ') + 1); if ("byte".equals(rawType) || "short".equals(rawType) || "int".equals(rawType) || "long".equals(rawType) || "float".equals(rawType) || "double".equals(rawType) || "char".equals(rawType) || "boolean".equals(rawType)) { // only mind of what looks like constants that we can keep without hogging memory decl.text += "public static final " + rawType + " " + javaName + " = " + javaName + "();\n"; } } } if (dcl.indices > 0) { // in the case of arrays, also add a pointer accessor tokens.index = backIndex; dcl = declarator(context, null, -1, false, n, true, false); String indices = ""; for (int i = 0; i < (metadcl == null ? 0 : metadcl.indices); i++) { if (i > 0) { indices += ", "; } indices += "int " + (char)('i' + i); } if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } if (metadcl != null && metadcl.cppName.length() > 0) { decl.text += metadcl.indices == 0 ? "@Name(\"" + metadcl.cppName + "." + shortName + "\") " : "@Name({\"" + metadcl.cppName + "\", \"." + shortName + "\"}) "; dcl.type.annotations = dcl.type.annotations.replaceAll("@Name\\(.*\\) ", ""); javaName = metadcl.javaName + "_" + shortName; } decl.text += "@MemberGetter " + modifiers + dcl.type.annotations.replace("@ByVal ", "@ByRef ") + dcl.type.javaName + " " + javaName + "(" + indices + ");\n"; } decl.signature = dcl.signature; if (info != null && info.javaText != null) { decl.text = info.javaText; decl.declarator = null; } while (!tokens.get().match(Token.EOF, ';')) { tokens.next(); } tokens.next(); String comment = commentAfter(); if (first) { first = false; declList.spacing = spacing; decl.text = comment + decl.text; } decl.variable = true; declList.add(decl); } declList.spacing = null; return true; } boolean macro(Context context, DeclarationList declList) throws ParserException { int backIndex = tokens.index; if (!tokens.get().match('#')) { return false; } tokens.raw = true; String spacing = tokens.get().spacing; Token keyword = tokens.next(); // parse all of the macro to find its last token tokens.next(); int beginIndex = tokens.index; for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (token.spacing.indexOf('\n') >= 0) { break; } } int endIndex = tokens.index; while (tokens.get(-1).match(Token.COMMENT)) { tokens.index--; } int lastIndex = tokens.index; Declaration decl = new Declaration(); if (keyword.match(Token.DEFINE) && beginIndex < endIndex) { tokens.index = beginIndex; String macroName = tokens.get().value; Token first = tokens.next(); boolean hasArgs = first.spacing.length() == 0 && first.match('('); List infoList = infoMap.get(macroName); for (Info info : infoList.size() > 0 ? infoList : Arrays.asList(new Info[] { null })) { if (info != null && info.skip) { break; } else if ((info == null && (hasArgs || beginIndex + 1 == endIndex)) || (info != null && info.cppText == null && info.cppTypes != null && info.cppTypes.length == 0)) { // save declaration for expansion info = new Info(macroName).cppText(""); tokens.index = backIndex; for (Token token = tokens.get(); tokens.index < endIndex; token = tokens.next()) { info.cppText += token.match("\n") ? token : token.spacing + token; } infoMap.putFirst(info); break; } else if (info != null && info.cppText == null && info.cppTypes != null && info.cppTypes.length > (hasArgs ? 0 : 1)) { // declare as a static native method List prevDcl = new ArrayList(); for (int n = -1; n < Integer.MAX_VALUE; n++) { int count = 1; tokens.index = beginIndex + 2; String params = "("; for (Token token = tokens.get(); hasArgs && tokens.index < lastIndex && count < info.cppTypes.length; token = tokens.next()) { if (token.match(Token.IDENTIFIER)) { String type = info.cppTypes[count]; String name = token.value; if (name.equals("...")) { name = "arg" + count; } params += type + " " + name; if (++count < info.cppTypes.length) { params += ", "; } } else if (token.match(')')) { break; } } while (count < info.cppTypes.length) { String type = info.cppTypes[count]; String name = "arg" + count; params += type + " " + name; if (++count < info.cppTypes.length) { params += ", "; } } params += ")"; Declarator dcl = new Parser(this, info.cppTypes[0] + " " + macroName + params).declarator(context, null, n, false, 0, false, false); for (int i = 0; i < info.cppNames.length; i++) { if (macroName.equals(info.cppNames[i]) && info.javaNames != null) { macroName = "@Name(\"" + info.cppNames[0] + "\") " + info.javaNames[i]; break; } } boolean found = false; for (Declarator d : prevDcl) { found |= dcl.signature.equals(d.signature); } if (!found) { decl.text += "public static native " + dcl.type.annotations + dcl.type.javaName + " " + macroName + dcl.parameters.list + ";\n"; } else if (found && n > 0) { break; } prevDcl.add(dcl); } } else if (info == null || (info.cppText == null && (info.cppTypes == null || info.cppTypes.length == 1))) { // declare as a static final variable String value = ""; String type = "int"; String cat = ""; tokens.index = beginIndex + 1; Token prevToken = new Token(); boolean translate = true; for (Token token = tokens.get(); tokens.index < lastIndex; token = tokens.next()) { if (token.match(Token.STRING)) { type = "String"; cat = " + "; break; } else if (token.match(Token.FLOAT)) { type = "double"; cat = ""; break; } else if (token.match(Token.INTEGER) && token.value.endsWith("L")) { type = "long"; cat = ""; break; } else if ((prevToken.match(Token.IDENTIFIER, '>') && token.match(Token.IDENTIFIER, '(')) || token.match('{', '}')) { translate = false; } prevToken = token; } if (info != null) { if (info.cppTypes != null) { Declarator dcl = new Parser(this, info.cppTypes[0]).declarator(context, null, -1, false, 0, false, true); type = dcl.type.annotations + dcl.type.javaName; } for (int i = 0; i < info.cppNames.length; i++) { if (macroName.equals(info.cppNames[i]) && info.javaNames != null) { macroName = "@Name(\"" + info.cppNames[0] + "\") " + info.javaNames[i]; break; } } translate = info.translate; } tokens.index = beginIndex + 1; if (translate) { for (Token token = tokens.get(); tokens.index < lastIndex; token = tokens.next()) { value += token.spacing + token + (tokens.index + 1 < lastIndex ? cat : ""); } value = translate(value); } else { decl.text += "public static native @MemberGetter " + type + " " + macroName + "();\n"; value = " " + macroName + "()"; } int i = type.lastIndexOf(' '); if (i >= 0) { type = type.substring(i + 1); } if (value.length() > 0) { decl.text += "public static final " + type + " " + macroName + " =" + value + ";\n"; } decl.signature = macroName; } if (info != null && info.javaText != null) { decl.text = info.javaText; break; } } } if (decl.text.length() == 0) { // output whatever we did not process as comment tokens.index = beginIndex; int n = spacing.lastIndexOf('\n') + 1; decl.text += "// " + spacing.substring(n) + "#" + keyword.spacing + keyword; for (Token token = tokens.get(); tokens.index < lastIndex; token = tokens.next()) { decl.text += token.match("\n") ? "\n// " : token.spacing + token.toString().replace("\n", "\n//"); } spacing = spacing.substring(0, n); } if (decl.text.length() > 0) { tokens.index = lastIndex; String comment = commentAfter(); decl.text = comment + decl.text; } tokens.raw = false; declList.spacing = spacing; declList.add(decl); declList.spacing = null; return true; } boolean typedef(Context context, DeclarationList declList) throws ParserException { String spacing = tokens.get().spacing; if (!tokens.get().match(Token.TYPEDEF)) { return false; } Declaration decl = new Declaration(); Declarator dcl = declarator(context, null, 0, false, 0, true, false); tokens.next(); String typeName = dcl.type.cppName, defName = dcl.cppName; int namespace = defName.lastIndexOf("::"); if (context.namespace != null && namespace < 0) { defName = context.namespace + "::" + defName; } Info info = infoMap.getFirst(defName); if (dcl.definition != null) { // a function pointer or something decl = dcl.definition; if (dcl.javaName.length() > 0 && context.javaName != null) { dcl.javaName = context.javaName + "." + dcl.javaName; } if (info == null || !info.skip) { info = info != null ? new Info(info).cppNames(defName) : new Info(defName); infoMap.put(info.valueTypes(dcl.javaName) .pointerTypes((dcl.indirections > 0 ? "@ByPtrPtr " : "") + dcl.javaName)); } } else if (typeName.equals("void")) { // some opaque data type if (info == null || !info.skip) { if (dcl.indirections > 0) { decl.text += "@Namespace @Name(\"void\") "; info = info != null ? new Info(info).cppNames(defName) : new Info(defName); infoMap.put(info.valueTypes(dcl.javaName).pointerTypes("@ByPtrPtr " + dcl.javaName)); } else if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } decl.text += "@Opaque public static class " + dcl.javaName + " extends Pointer {\n" + " /** Empty constructor. Calls {@code super((Pointer)null)}. */\n" + " public " + dcl.javaName + "() { super((Pointer)null); }\n" + " /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */\n" + " public " + dcl.javaName + "(Pointer p) { super(p); }\n" + "}"; } } else { // point back to original type info = infoMap.getFirst(typeName); if (info == null || !info.skip) { info = info != null ? new Info(info).cppNames(defName) : new Info(defName); if (info.cppTypes == null && info.annotations != null) { // set original C++ type for typedef of types we want to use with adapters String s = typeName; if (dcl.type.constValue && !s.startsWith("const ")) { s = "const " + s; } if (dcl.type.constPointer && !s.endsWith(" const")) { s = s + " const"; } if (dcl.type.indirections > 0) { for (int i = 0; i < dcl.type.indirections; i++) { s += "*"; } } if (dcl.type.reference) { s += "&"; } info.cppNames(defName, s).cppTypes(s); } if (info.valueTypes == null && dcl.indirections > 0) { info.valueTypes(info.pointerTypes != null ? info.pointerTypes : new String[] {typeName}); info.pointerTypes("PointerPointer"); } else if (info.pointerTypes == null) { info.pointerTypes(typeName); } if (info.annotations == null) { info.cast(!dcl.cppName.equals(info.pointerTypes[0])); } infoMap.put(info); } } if (info != null && info.javaText != null) { decl.text = info.javaText; } String comment = commentAfter(); decl.text = comment + decl.text; declList.spacing = spacing; declList.add(decl); declList.spacing = null; return true; } boolean using(Context context, DeclarationList declList) throws ParserException { if (!tokens.get().match(Token.USING)) { return false; } String spacing = tokens.get().spacing; boolean namespace = tokens.get(1).match(Token.NAMESPACE); Declarator dcl = declarator(context, null, 0, false, 0, true, false); tokens.next(); context.usingList.add(dcl.type.cppName + (namespace ? "::" : "")); Declaration decl = new Declaration(); if (dcl.definition != null) { decl = dcl.definition; } String comment = commentAfter(); decl.text = comment + decl.text; declList.spacing = spacing; declList.add(decl); declList.spacing = null; return true; } boolean group(Context context, DeclarationList declList) throws ParserException { int backIndex = tokens.index; String spacing = tokens.get().spacing; boolean typedef = tokens.get().match(Token.TYPEDEF); boolean foundGroup = false, friend = false; Context ctx = new Context(context); for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (token.match(Token.CLASS, Token.INTERFACE, Token.__INTERFACE, Token.STRUCT, Token.UNION)) { foundGroup = true; ctx.inaccessible = token.match(Token.CLASS); break; } else if (token.match(Token.FRIEND)) { friend = true; } else if (!token.match(Token.IDENTIFIER)) { break; } } if (!foundGroup) { tokens.index = backIndex; return false; } tokens.next().expect(Token.IDENTIFIER, '{', "::"); if (!tokens.get().match('{') && tokens.get(1).match(Token.IDENTIFIER) && (typedef || !tokens.get(2).match(';'))) { tokens.next(); } Type type = type(context); List baseClasses = new ArrayList(); Declaration decl = new Declaration(); decl.text = type.annotations; String name = type.javaName; boolean anonymous = !typedef && type.cppName.length() == 0, derivedClass = false, skipBase = false; if (type.cppName.length() > 0 && tokens.get().match(':')) { derivedClass = true; for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { boolean accessible = false; if (token.match(Token.VIRTUAL)) { continue; } else if (token.match(Token.PRIVATE, Token.PROTECTED, Token.PUBLIC)) { accessible = token.match(Token.PUBLIC); tokens.next(); } Type t = type(context); Info info = infoMap.getFirst(t.cppName); if (info != null && info.skip) { skipBase = true; } if (accessible) { baseClasses.add(t); } if (tokens.get().expect(',', '{').match('{')) { break; } } } if (typedef && type.indirections > 0) { // skip pointer typedef while (!tokens.get().match(';', Token.EOF)) { tokens.next(); } } if (!tokens.get().match('{', ';')) { tokens.index = backIndex; return false; } int startIndex = tokens.index; List variables = new ArrayList(); String originalName = type.cppName; if (body() != null && !tokens.get().match(';')) { if (typedef) { for (Token prevToken = null, token = tokens.get(); !token.match(Token.EOF); prevToken = token, token = tokens.next()) { if (token.match(';')) { decl.text += token.spacing; break; } else if ((prevToken == null || prevToken.match('}', ',')) && token.match(Token.IDENTIFIER)) { // use typedef name, unless it's something weird like a pointer name = type.javaName = type.cppName = token.value; } } } else { int index = tokens.index - 1; for (int n = 0; n < Integer.MAX_VALUE; n++) { tokens.index = index; Declarator dcl = declarator(context, null, -1, false, n, false, true); if (dcl == null) { break; } else { // declares variable, treat as anonymous anonymous = true; variables.add(dcl); } } int n = spacing.lastIndexOf('\n'); if (n >= 0) { decl.text += spacing.substring(0, n); } } } int namespace = type.cppName.lastIndexOf("::"); if (context.namespace != null && namespace < 0) { type.cppName = context.namespace + "::" + type.cppName; originalName = context.namespace + "::" + originalName; } Info info = infoMap.getFirst(type.cppName); if (((info == null || info.base == null) && skipBase) || (info != null && info.skip)) { decl.text = ""; declList.add(decl); return true; } else if (info != null && info.pointerTypes != null && info.pointerTypes.length > 0) { type.javaName = info.pointerTypes[0]; name = context.shorten(type.javaName); } else if (info == null) { if (type.javaName.length() > 0 && context.javaName != null) { type.javaName = context.javaName + "." + type.javaName; } infoMap.put(info = new Info(type.cppName).pointerTypes(type.javaName)); } Type base = new Type("Pointer"); Iterator it = baseClasses.iterator(); while (it.hasNext()) { Type next = it.next(); Info nextInfo = infoMap.getFirst(next.cppName); if (nextInfo == null || !nextInfo.flatten) { base = next; it.remove(); break; } } String casts = ""; if (baseClasses.size() > 0) { for (Type t : baseClasses) { casts += " public " + t.javaName + " as" + t.javaName + "() { return as" + t.javaName + "(this); }\n" + " @Namespace public static native @Name(\"static_cast<" + t.cppName + "*>\") " + t.javaName + " as" + t.javaName + "(" + type.javaName + " pointer);\n"; } } decl.signature = type.javaName; tokens.index = startIndex; if (name.length() > 0 && tokens.get().match(';')) { // incomplete type (forward or friend declaration) tokens.next(); if (friend) { decl.text = ""; declList.add(decl); return true; } else if (info != null && info.base != null) { base.javaName = info.base; } String fullName = context.namespace != null ? context.namespace + "::" + name : name; if (!fullName.equals(type.cppName)) { decl.text += "@Name(\"" + (context.javaName == null || namespace < 0 ? type.cppName : type.cppName.substring(namespace + 2)) + "\") "; } else if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } decl.text += "@Opaque public static class " + name + " extends " + base.javaName + " {\n" + " /** Empty constructor. Calls {@code super((Pointer)null)}. */\n" + " public " + name + "() { super((Pointer)null); }\n" + " /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */\n" + " public " + name + "(Pointer p) { super(p); }\n" + "}"; decl.type = type; decl.incomplete = true; String comment = commentAfter(); decl.text = comment + decl.text; declList.spacing = spacing; declList.add(decl); declList.spacing = null; return true; } else if (tokens.get().match('{')) { tokens.next(); } if (type.cppName.length() > 0) { // name of the typedef type ctx.namespace = type.cppName; // used to match constructors ctx.cppName = originalName; } if (!anonymous) { // used for setter methods ctx.javaName = type.javaName; } if (info != null && info.virtualize) { ctx.virtualize = true; } DeclarationList declList2 = new DeclarationList(); if (variables.size() == 0) { declarations(ctx, declList2); } else for (Declarator var : variables) { if (context.variable != null) { var.cppName = context.variable.cppName + "." + var.cppName; var.javaName = context.variable.javaName + "_" + var.javaName; } ctx.variable = var; declarations(ctx, declList2); } String modifiers = "public static "; boolean implicitConstructor = true, defaultConstructor = false, longConstructor = false, pointerConstructor = false, abstractClass = info != null && info.purify && !ctx.virtualize, havePureConst = false, haveVariables = false; for (Declaration d : declList2) { if (d.declarator != null && d.declarator.type != null && d.declarator.type.constructor) { implicitConstructor = false; Declarator[] paramDcls = d.declarator.parameters.declarators; defaultConstructor |= (paramDcls.length == 0 || (paramDcls.length == 1 && paramDcls[0].type.javaName.equals("void"))) && !d.inaccessible; longConstructor |= paramDcls.length == 1 && paramDcls[0].type.javaName.equals("long") && !d.inaccessible; pointerConstructor |= paramDcls.length == 1 && paramDcls[0].type.javaName.equals("Pointer") && !d.inaccessible; } abstractClass |= d.abstractMember; havePureConst |= d.constMember && d.abstractMember; haveVariables |= d.variable; } if (havePureConst && ctx.virtualize) { modifiers = "@Const " + modifiers; } if (!anonymous) { String fullName = context.namespace != null ? context.namespace + "::" + name : name; if (!fullName.equals(type.cppName)) { decl.text += "@Name(\"" + type.cppName + "\") "; } else if (context.namespace != null && context.javaName == null) { decl.text += "@Namespace(\"" + context.namespace + "\") "; } if ((!implicitConstructor || derivedClass) && haveVariables) { decl.text += "@NoOffset "; } if (info != null && info.base != null) { base.javaName = info.base; } decl.text += modifiers + "class " + name + " extends " + base.javaName + " {\n" + " static { Loader.load(); }\n"; if (implicitConstructor && (!abstractClass || ctx.virtualize)) { decl.text += " /** Default native constructor. */\n" + " public " + name + "() { super((Pointer)null); allocate(); }\n" + " /** Native array allocator. Access with {@link Pointer#position(long)}. */\n" + " public " + name + "(long size) { super((Pointer)null); allocateArray(size); }\n" + " /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */\n" + " public " + name + "(Pointer p) { super(p); }\n" + " private native void allocate();\n" + " private native void allocateArray(long size);\n" + " @Override public " + name + " position(long position) {\n" + " return (" + name + ")super.position(position);\n" + " }\n"; } else { if (!pointerConstructor) { decl.text += " /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */\n" + " public " + name + "(Pointer p) { super(p); }\n"; } if (defaultConstructor && (!abstractClass || ctx.virtualize) && !longConstructor) { decl.text += " /** Native array allocator. Access with {@link Pointer#position(long)}. */\n" + " public " + name + "(long size) { super((Pointer)null); allocateArray(size); }\n" + " private native void allocateArray(long size);\n" + " @Override public " + name + " position(long position) {\n" + " return (" + name + ")super.position(position);\n" + " }\n"; } } declList.spacing = spacing; decl.text = declList.rescan(decl.text + casts + "\n"); declList.spacing = null; } for (Type base2 : baseClasses) { Info baseInfo = infoMap.getFirst(base2.cppName); if (baseInfo != null && baseInfo.flatten && baseInfo.javaText != null) { String text = baseInfo.javaText; int start = text.indexOf('{'); for (int n = 0; n < 2; start++) { int c = text.charAt(start); if (c == '\n') { n++; } else if (!Character.isWhitespace(c)) { n = 0; } } int end = text.lastIndexOf('}'); decl.text += text.substring(start, end).replaceAll("(\\s+)" + base2.javaName + "(\\s+)", "$1" + type.javaName + "$2"); } } for (Declaration d : declList2) { if (!d.inaccessible && (d.declarator == null || d.declarator.type == null || !d.declarator.type.constructor || !abstractClass)) { decl.text += d.text; } } if (!anonymous) { decl.text += tokens.get().spacing + '}'; } for (Token token = tokens.next(); !token.match(Token.EOF); token = tokens.next()) { if (token.match(';')) { decl.text += token.spacing; break; } } tokens.next(); decl.type = type; if (info != null && info.javaText != null) { decl.text = info.javaText; } else if (info != null && info.flatten) { info.javaText = decl.text; } declList.add(decl); return true; } boolean enumeration(Context context, DeclarationList declList) throws ParserException { int backIndex = tokens.index; String enumSpacing = tokens.get().spacing; boolean typedef = tokens.get().match(Token.TYPEDEF); boolean foundEnum = false; for (Token token = tokens.get(); !token.match(Token.EOF); token = tokens.next()) { if (token.match(Token.ENUM)) { foundEnum = true; break; } else if (!token.match(Token.IDENTIFIER)) { break; } } if (!foundEnum) { tokens.index = backIndex; return false; } String enumType = "enum"; if (tokens.get(1).match(Token.CLASS, Token.STRUCT)) { enumType += " " + tokens.next(); } if (typedef && !tokens.get(1).match('{') && tokens.get(2).match(Token.IDENTIFIER)) { tokens.next(); } int count = 0; boolean longenum = false; String cppType = "int"; String javaType = "int"; String separator = ""; String enumPrefix = "public static final " + javaType; String countPrefix = " "; String enumerators = ""; String extraText = ""; String name = ""; Token token = tokens.next().expect(Token.IDENTIFIER, '{', ':', ';'); if (token.match(Token.IDENTIFIER)) { name = token.value; token = tokens.next(); } if (token.match(':')) { token = tokens.next(); Type type = type(context); cppType = type.cppName; javaType = type.javaName; enumPrefix = "public static final " + javaType; token = tokens.get(); } if (!typedef && token.match(';')) { // skip for loop } else if (!token.match('{')) { // not an enum tokens.index = backIndex; return false; } else for (token = tokens.next(); !token.match(Token.EOF, '}'); token = tokens.get()) { String comment = commentBefore(); if (macro(context, declList)) { Declaration macroDecl = declList.remove(declList.size() - 1); extraText += comment + macroDecl.text; if (separator.equals(",") && !macroDecl.text.trim().startsWith("//")) { separator = ";"; enumPrefix = "\npublic static final " + javaType; } continue; } Token enumerator = tokens.get(); String cppName = enumerator.value; String javaName = cppName; if (context.namespace != null) { cppName = context.namespace + "::" + cppName; } Info info = infoMap.getFirst(cppName); if (info != null && info.javaNames != null && info.javaNames.length > 0) { javaName = info.javaNames[0]; } else if (info == null) { infoMap.put(info = new Info(cppName)); } String spacing2 = " "; if (tokens.next().match('=')) { spacing2 = tokens.get().spacing; countPrefix = " "; int count2 = 0; Token prevToken = new Token(); boolean translate = true; for (token = tokens.next(); !token.match(Token.EOF, ',', '}') || count2 > 0; token = tokens.next()) { if (token.match(Token.INTEGER) && token.value.endsWith("L")) { longenum = true; } countPrefix += token.spacing + token; if (token.match('(')) { count2++; } else if (token.match(')')) { count2--; } if ((prevToken.match(Token.IDENTIFIER) && token.match('(')) || token.match('{', '}')) { translate = false; } prevToken = token; } try { count = Integer.parseInt(countPrefix.trim()); countPrefix = " "; } catch (NumberFormatException e) { count = 0; if (translate) { countPrefix = translate(countPrefix); } else { if (separator.equals(",")) { separator = ";"; } extraText = "\npublic static native @MemberGetter " + javaType + " " + javaName + "();\n"; enumPrefix = "public static final " + javaType; countPrefix = " " + javaName + "()"; } } } enumerators += separator + extraText + enumPrefix + comment; separator = ","; enumPrefix = ""; extraText = ""; comment = commentAfter(); if (comment.length() == 0 && tokens.get().match(',')) { tokens.next(); comment = commentAfter(); } String spacing = enumerator.spacing; if (comment.length() > 0) { enumerators += spacing + comment; int newline = spacing.lastIndexOf('\n'); if (newline >= 0) { spacing = spacing.substring(newline + 1); } } if (spacing.length() == 0 && !enumerators.endsWith(",")) { spacing = " "; } enumerators += spacing + javaName + spacing2 + "=" + countPrefix; if (countPrefix.trim().length() > 0) { if (count > 0) { enumerators += " + " + count; } } else { enumerators += count; } count++; } String comment = commentBefore(); Declaration decl = new Declaration(); token = tokens.get(); if (!token.match(';')) { token = tokens.next(); } if (token.match(Token.IDENTIFIER)) { // XXX: If !typedef, this is a variable declaration with anonymous type name = token.value; token = tokens.next(); } if (context.namespace != null) { name = context.namespace + "::" + name; } Info info = infoMap.getFirst(name); if (info != null && info.skip) { decl.text = enumSpacing; } else { decl.text += enumSpacing + "/** " + enumType + " " + name + " */\n"; int newline = enumSpacing.lastIndexOf('\n'); if (newline >= 0) { enumSpacing = enumSpacing.substring(newline + 1); } decl.text += enumSpacing + enumerators + token.expect(';').spacing + ";"; if (longenum) { decl.text = decl.text.replace(" " + javaType, " long"); cppType = "long long"; javaType = "long"; } if (name.length() > 0) { Info info2 = infoMap.getFirst(cppType); infoMap.put(new Info(info2).cast().cppNames(name)); } decl.text += extraText + comment; } declList.add(decl); tokens.next(); return true; } boolean namespace(Context context, DeclarationList declList) throws ParserException { if (!tokens.get().match(Token.NAMESPACE)) { return false; } Declaration decl = new Declaration(); String spacing = tokens.get().spacing; String name = null; tokens.next(); if (tokens.get().match(Token.IDENTIFIER)) { // get the name, unless anonymous name = tokens.get().value; tokens.next(); } if (tokens.get().match('=')) { // deal with namespace aliases tokens.next(); Type type = type(context); context.namespaceMap.put(name, type.cppName); tokens.get().expect(';'); tokens.next(); return true; } tokens.get().expect('{'); tokens.next(); if (tokens.get().spacing.indexOf('\n') < 0) { tokens.get().spacing = spacing; } context = new Context(context); context.namespace = name == null ? null : context.namespace != null ? context.namespace + "::" + name : name; declarations(context, declList); decl.text += tokens.get().expect('}').spacing; tokens.next(); declList.add(decl); return true; } boolean extern(Context context, DeclarationList declList) throws ParserException { if (!tokens.get().match(Token.EXTERN) || !tokens.get(1).match(Token.STRING)) { return false; } String spacing = tokens.get().spacing; Declaration decl = new Declaration(); tokens.next().expect("\"C\""); if (!tokens.next().match('{')) { tokens.get().spacing = spacing; declList.add(decl); return true; } tokens.next(); declarations(context, declList); tokens.get().expect('}'); tokens.next(); declList.add(decl); return true; } void declarations(Context context, DeclarationList declList) throws ParserException { for (Token token = tokens.get(); !token.match(Token.EOF, '}'); token = tokens.get()) { if (token.match(Token.PRIVATE, Token.PROTECTED, Token.PUBLIC) && tokens.get(1).match(':')) { context.inaccessible = !token.match(Token.PUBLIC); tokens.next(); tokens.next(); continue; } Context ctx = context; String comment = commentBefore(); token = tokens.get(); String spacing = token.spacing; TemplateMap map = template(ctx); if (map != null) { token = tokens.get(); token.spacing = spacing; ctx = new Context(ctx); ctx.templateMap = map; } Declaration decl = new Declaration(); if (comment != null && comment.length() > 0) { decl.inaccessible = ctx.inaccessible; decl.text = comment; declList.add(decl); } int startIndex = tokens.index; declList.infoMap = infoMap; declList.context = ctx; declList.templateMap = map; declList.infoIterator = null; declList.spacing = null; do { if (map != null && declList.infoIterator != null && declList.infoIterator.hasNext()) { Info info = declList.infoIterator.next(); if (info == null) { continue; } Type type = new Parser(this, info.cppNames[0]).type(context); if (type.arguments == null) { continue; } int count = 0; for (Map.Entry e : map.entrySet()) { if (count < type.arguments.length) { Type t = type.arguments[count++]; String s = t.cppName; if (t.constValue && !s.startsWith("const ")) { s = "const " + s; } if (t.constPointer && !s.endsWith(" const")) { s = s + " const"; } if (t.indirections > 0) { for (int i = 0; i < t.indirections; i++) { s += "*"; } } if (t.reference) { s += "&"; } t.cppName = s; e.setValue(t); } } tokens.index = startIndex; } if (!tokens.get().match(';') && !macro(ctx, declList) && !extern(ctx, declList) && !namespace(ctx, declList) && !enumeration(ctx, declList) && !group(ctx, declList) && !typedef(ctx, declList) && !using(ctx, declList) && !function(ctx, declList) && !variable(ctx, declList)) { spacing = tokens.get().spacing; if (attribute() != null) { tokens.get().spacing = spacing; } else { throw new ParserException(token.file + ":" + token.lineNumber + ": Could not parse declaration at '" + token + "'"); } } while (tokens.get().match(';') && !tokens.get().match(Token.EOF)) { tokens.next(); } } while (declList.infoIterator != null && declList.infoIterator.hasNext()); } // for comments at the end without declarations String comment = commentBefore(); Declaration decl = new Declaration(); if (comment != null && comment.length() > 0) { decl.text = comment; declList.add(decl); } } void parse(Context context, DeclarationList declList, String[] includePath, String include, boolean isCFile) throws IOException, ParserException { List tokenList = new ArrayList(); File file = null; String filename = include; if (filename.startsWith("<") && filename.endsWith(">")) { filename = filename.substring(1, filename.length() - 1); } else { File f = new File(filename); if (f.exists()) { file = f; } } if (file == null && includePath != null) { for (String path : includePath) { File f = new File(path, filename).getCanonicalFile(); if (f.exists()) { file = f; break; } } } if (file == null) { file = new File(filename); } Info info = infoMap.getFirst(file.getName()); if (info != null && info.skip && info.linePatterns == null) { return; } else if (!file.exists()) { throw new FileNotFoundException("Could not parse \"" + file + "\": File does not exist"); } logger.info("Parsing " + file); Token token = new Token(); token.type = Token.COMMENT; token.value = "\n// Parsed from " + include + "\n\n"; tokenList.add(token); Tokenizer tokenizer = new Tokenizer(file); if (info != null && info.linePatterns != null) { tokenizer.filterLines(info.linePatterns, info.skip); } while (!(token = tokenizer.nextToken()).isEmpty()) { if (token.type == -1) { token.type = Token.COMMENT; } tokenList.add(token); } if (lineSeparator == null) { lineSeparator = tokenizer.lineSeparator; } tokenizer.close(); token = new Token(); token.type = Token.COMMENT; token.spacing = "\n"; tokenList.add(token); tokens = new TokenIndexer(infoMap, tokenList.toArray(new Token[tokenList.size()]), isCFile); declarations(context, declList); } public File parse(String outputDirectory, String[] classPath, Class cls) throws IOException, ParserException { return parse(new File(outputDirectory), classPath, cls); } public File parse(File outputDirectory, String[] classPath, Class cls) throws IOException, ParserException { ClassProperties allProperties = Loader.loadProperties(cls, properties, true); ClassProperties clsProperties = Loader.loadProperties(cls, properties, false); // Capture c-includes from "class" and "all" properties List cIncludes = new ArrayList<>(); cIncludes.addAll(clsProperties.get("platform.cinclude")); cIncludes.addAll(allProperties.get("platform.cinclude")); // Capture class includes List clsIncludes = new ArrayList(); clsIncludes.addAll(clsProperties.get("platform.include")); clsIncludes.addAll(clsProperties.get("platform.cinclude")); // Capture all includes List allIncludes = new ArrayList(); allIncludes.addAll(allProperties.get("platform.include")); allIncludes.addAll(allProperties.get("platform.cinclude")); List allTargets = allProperties.get("target"); List clsTargets = clsProperties.get("target"); List clsHelpers = clsProperties.get("helper"); String target = clsTargets.get(0); // there can only be one List allInherited = allProperties.getInheritedClasses(); infoMap = new InfoMap(); for (Class c : allInherited) { try { ((InfoMapper)c.newInstance()).map(infoMap); } catch (ClassCastException | InstantiationException | IllegalAccessException e) { // fail silently as if the interface wasn't implemented } } leafInfoMap = new InfoMap(); try { ((InfoMapper)cls.newInstance()).map(leafInfoMap); } catch (ClassCastException | InstantiationException | IllegalAccessException e) { // fail silently as if the interface wasn't implemented } infoMap.putAll(leafInfoMap); String version = Generator.class.getPackage().getImplementationVersion(); if (version == null) { version = "unknown"; } String text = "// Targeted by JavaCPP version " + version + ": DO NOT EDIT THIS FILE\n\n"; int n = target.lastIndexOf('.'); if (n >= 0) { text += "package " + target.substring(0, n) + ";\n\n"; } List infoList = leafInfoMap.get(null); for (Info info : infoList) { if (info.javaText != null && info.javaText.startsWith("import")) { text += info.javaText + "\n"; } } text += "import java.nio.*;\n" + "import org.bytedeco.javacpp.*;\n" + "import org.bytedeco.javacpp.annotation.*;\n\n"; for (String s : allTargets) { if (!target.equals(s)) { text += "import static " + s + ".*;\n"; } } if (allTargets.size() > 1) { text += "\n"; } text += "public class " + target.substring(n + 1) + " extends " + (clsHelpers.size() > 0 && clsIncludes.size() > 0 ? clsHelpers.get(0) : cls.getCanonicalName()) + " {\n" + " static { Loader.load(); }\n"; String targetPath = target.replace('.', File.separatorChar); File targetFile = new File(outputDirectory, targetPath + ".java"); logger.info("Targeting " + targetFile); Context context = new Context(); context.infoMap = infoMap; String[] includePath = classPath; n = targetPath.lastIndexOf(File.separatorChar); if (n >= 0) { includePath = classPath.clone(); for (int i = 0; i < includePath.length; i++) { includePath[i] += File.separator + targetPath.substring(0, n); } } List paths = allProperties.get("platform.includepath"); String[] includePaths = paths.toArray(new String[paths.size() + includePath.length]); System.arraycopy(includePath, 0, includePaths, paths.size(), includePath.length); DeclarationList declList = new DeclarationList(); for (String include : allIncludes) { if (!clsIncludes.contains(include)) { boolean isCFile = cIncludes.contains(include); parse(context, declList, includePaths, include, isCFile); } } declList = new DeclarationList(declList); if (clsIncludes.size() > 0) { containers(context, declList); for (String include : clsIncludes) { if (allIncludes.contains(include)) { boolean isCFile = cIncludes.contains(include); parse(context, declList, includePaths, include, isCFile); } } } File targetDir = targetFile.getParentFile(); if (targetDir != null) { targetDir.mkdirs(); } final String newline = lineSeparator != null ? lineSeparator : "\n"; try (Writer out = new FileWriter(targetFile) { @Override public Writer append(CharSequence text) throws IOException { return super.append(((String)text).replace("\n", newline).replace("\\u", "\\u005Cu")); } }) { out.append(text); for (Info info : infoList) { if (info.javaText != null && !info.javaText.startsWith("import")) { out.append(info.javaText + "\n"); } } for (Declaration d : declList) { out.append(d.text); } out.append("\n}\n").close(); } return targetFile; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy