private static Method notBogus(Method method, String propertyName, Class<?> paramType) { if (method == null) return null; Container reader = Container.readerAnnotation(method); if (reader != null && (!reader.name().equals(propertyName) || !reader.enabled() || !method.getReturnType().isAssignableFrom(paramType))) { return null; } else { return method; } }
private static MetaData buildMetaData(Class<? extends QObject> clazz) { MetaData metaData = new MetaData(); List<Method> slots = new ArrayList<Method>(); Hashtable<String, Method> propertyReaders = new Hashtable<String, Method>(); Hashtable<String, Method> propertyWriters = new Hashtable<String, Method>(); Hashtable<String, Object> propertyDesignables = new Hashtable<String, Object>(); Hashtable<String, Method> propertyResetters = new Hashtable<String, Method>(); Hashtable<String, Boolean> propertyUser = new Hashtable<String, Boolean>(); // First we get all enums actually declared in the class Hashtable<String, Class<?>> enums = new Hashtable<String, Class<?>>(); int enumConstantCount = queryEnums(clazz, enums); int enumCount = enums.size(); // Get the size before we add external enums Method declaredMethods[] = clazz.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { if (!declaredMethod.isAnnotationPresent(QtBlockedSlot.class) && ((declaredMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC)) { // If we can't convert the type, we don't list it String methodParameters = methodParameters(declaredMethod); String returnType = declaredMethod.getReturnType().getName(); if ((methodParameters.equals("") || !internalTypeNameOfParameters(declaredMethod, 1).equals("")) && (returnType.equals("") || returnType.equals("void") || !internalTypeNameOfClass(declaredMethod.getReturnType(), 0).equals(""))) { slots.add(declaredMethod); } } // Rules for readers: // 1. Zero arguments // 2. Return something other than void // 3. We can convert the type Container reader = Container.readerAnnotation(declaredMethod); { if (reader != null && reader.enabled() && isValidGetter(declaredMethod) && !internalTypeNameOfClass(declaredMethod.getReturnType(), 0).equals("")) { // If the return type of the property reader is not registered, then // we need to register the owner class in the meta object (in which case // it has to be a QObject) Class<?> returnType = declaredMethod.getReturnType(); int count = putEnumTypeInHash(returnType, enums); if (count < 0) { System.err.println( "Problem in property '" + reader.name() + "' in '" + clazz.getName() + "': Only enum types 1. declared inside QObject subclasses or the " + "Qt interface and 2. declared without the QtBlockedEnum annotation " + "are supported for properties"); continue; } propertyReaders.put(reader.name(), declaredMethod); propertyDesignables.put(reader.name(), isDesignable(declaredMethod, clazz)); propertyUser.put(reader.name(), isUser(declaredMethod)); } } // Rules for writers: // 1. Takes exactly one argument // 2. Return void // 3. We can convert the type Container writer = Container.writerAnnotation(declaredMethod); { if (writer != null && writer.enabled() && isValidSetter(declaredMethod)) { propertyWriters.put(writer.name(), declaredMethod); } } // Rules for resetters: // 1. No arguments // 2. Return void { Container resetter = Container.resetterAnnotation(declaredMethod); if (resetter != null && declaredMethod.getParameterTypes().length == 0 && declaredMethod.getReturnType() == Void.TYPE) { propertyResetters.put(resetter.name(), declaredMethod); } } // Check naming convention by looking for setXxx patterns, but only if it hasn't already been // annotated as a writer String declaredMethodName = declaredMethod.getName(); if (writer == null && reader == null // reader can't be a writer, cause the signature doesn't match, just an // optimization && declaredMethodName.startsWith("set") && declaredMethodName.length() > 3 && Character.isUpperCase(declaredMethodName.charAt(3)) && isValidSetter(declaredMethod)) { Class<?> paramType = declaredMethod.getParameterTypes()[0]; String propertyName = Character.toLowerCase(declaredMethodName.charAt(3)) + declaredMethodName.substring(4); if (!propertyReaders.containsKey(propertyName)) { // We need a reader as well, and the reader must not be annotated as disabled // The reader can be called 'xxx', 'getXxx', 'isXxx' or 'hasXxx' // (just booleans for the last two) Method readerMethod = notBogus(getMethod(clazz, propertyName, null), propertyName, paramType); if (readerMethod == null) readerMethod = notBogus( getMethod(clazz, "get" + capitalizeFirst(propertyName), null), propertyName, paramType); if (readerMethod == null && isBoolean(paramType)) readerMethod = notBogus( getMethod(clazz, "is" + capitalizeFirst(propertyName), null), propertyName, paramType); if (readerMethod == null && isBoolean(paramType)) readerMethod = notBogus( getMethod(clazz, "has" + capitalizeFirst(propertyName), null), propertyName, paramType); if (readerMethod != null) { // yay reader = Container.readerAnnotation(readerMethod); if (reader == null) { propertyReaders.put(propertyName, readerMethod); propertyWriters.put(propertyName, declaredMethod); propertyDesignables.put(propertyName, isDesignable(readerMethod, clazz)); propertyUser.put(propertyName, isUser(readerMethod)); } } } } } Field declaredFields[] = clazz.getDeclaredFields(); List<Field> signalFields = new ArrayList<Field>(); List<QtJambiInternal.ResolvedSignal> resolvedSignals = new ArrayList<QtJambiInternal.ResolvedSignal>(); for (Field declaredField : declaredFields) { if (QtJambiInternal.isSignal(declaredField.getType())) { // If we can't convert all the types we don't list the signal QtJambiInternal.ResolvedSignal resolvedSignal = QtJambiInternal.resolveSignal(declaredField, declaredField.getDeclaringClass()); String signalParameters = signalParameters(resolvedSignal); if (signalParameters.length() == 0 || internalTypeNameOfSignal(resolvedSignal.types, signalParameters, 1).length() != 0) { signalFields.add(declaredField); resolvedSignals.add(resolvedSignal); } } } metaData.signalsArray = signalFields.toArray(new Field[0]); { int functionCount = slots.size() + signalFields.size(); int propertyCount = propertyReaders.keySet().size(); int propertyNotifierCount = 0; // FIXME (see QTabWidget) int constructorCount = 0; // FIXME (see QObject) int metaObjectFlags = 0; // FIXME DynamicMetaObject // Until 4.7.x QtJambi used revision=1 however due to a change in the way // 4.7.x works some features of QtJambi stopped working. // revision 1 = MO_HEADER_LEN=10 // revision 2 (4.5.x) = MO_HEADER_LEN=12 (added: constructorCount, constructorData) // revision 3 = MO_HEADER_LEN=13 (added: flags) // revision 4 (4.6.x) = MO_HEADER_LEN=14 (added: signalCount) // revision 5 (4.7.x) = MO_HEADER_LEN=14 (normalization) // revision 6 (4.8.x) = MO_HEADER_LEN=14 (added support for qt_static_metacall) // revision 7 (5.0.x) = MO_HEADER_LEN=14 (Qt5 to break backwards compatibility) // The format is compatible to share the same encoding code // then we can change the revision to suit the Qt /// implementation we are working with. final int MO_HEADER_LEN = 14; // header size final int CLASSINFO_LEN = 2; // class info size int len = MO_HEADER_LEN + CLASSINFO_LEN; len += functionCount * 5; len += propertyCount * 3; len += propertyNotifierCount; len += enumCount * 4; len += enumConstantCount * 2; len += constructorCount * 5; metaData.metaData = new int[len + 1]; // add EOD <NUL> int intDataOffset = 0; // the offsets used by this descriptor are based on ints (32bit values) // Add static header metaData.metaData[0] = resolveMetaDataRevision(); // Revision // metaData[1] = 0 // class name (ints default to offset 0 into strings) intDataOffset += MO_HEADER_LEN; metaData.metaData[2] = 1; // class info count metaData.metaData[3] = intDataOffset; // class info offset intDataOffset += CLASSINFO_LEN; // Functions always start right after this header at offset MO_HEADER_LEN + CLASSINFO_LEN metaData.metaData[4] = functionCount; metaData.metaData[5] = functionCount == 0 ? 0 : intDataOffset; intDataOffset += functionCount * 5; // Each function takes 5 ints metaData.metaData[6] = propertyCount; metaData.metaData[7] = propertyCount == 0 ? 0 : intDataOffset; intDataOffset += (propertyCount * 3) + propertyNotifierCount; // Each property takes 3 ints and each propertNotifier 1 int // Enums metaData.metaData[8] = enumCount; metaData.metaData[9] = enumCount == 0 ? 0 : intDataOffset; intDataOffset += (enumCount * 4) + (enumConstantCount * 2); // Each enum takes 4 ints and each enumConst takes 2 ints // revision 1 ends here metaData.metaData[10] = constructorCount; metaData.metaData[11] = constructorCount == 0 ? 0 : intDataOffset; intDataOffset += constructorCount * 5; // revision 2 ends here metaData.metaData[12] = metaObjectFlags; // flags // revision 3 ends here metaData.metaData[13] = signalFields.size(); // signalCount // revision 4, 5 and 6 ends here int offset = 0; int metaDataOffset = MO_HEADER_LEN; // Header is currently 14 ints long Hashtable<String, Integer> strings = new Hashtable<String, Integer>(); List<String> stringsInOrder = new ArrayList<String>(); // Class name { String className = clazz.getName().replace(".", "::"); stringsInOrder.add(className); strings.put(className, offset); offset += className.length() + 1; } // Class info { offset += addString( metaData.metaData, strings, stringsInOrder, "__qt__binding_shell_language", offset, metaDataOffset++); offset += addString( metaData.metaData, strings, stringsInOrder, "Qt Jambi", offset, metaDataOffset++); } metaData.originalSignatures = new String[signalFields.size() + slots.size()]; // Signals (### make sure enum types are covered) for (int i = 0; i < signalFields.size(); ++i) { Field signalField = signalFields.get(i); QtJambiInternal.ResolvedSignal resolvedSignal = resolvedSignals.get(i); String javaSignalParameters = signalParameters(resolvedSignal); metaData.originalSignatures[i] = resolvedSignal.name + (javaSignalParameters.length() > 0 ? '<' + javaSignalParameters + '>' : ""); String signalParameters = internalTypeNameOfSignal(resolvedSignal.types, javaSignalParameters, 1); // Signal name offset += addString( metaData.metaData, strings, stringsInOrder, resolvedSignal.name + "(" + signalParameters + ")", offset, metaDataOffset++); // Signal parameters offset += addString( metaData.metaData, strings, stringsInOrder, signalParameters, offset, metaDataOffset++); // Signal type (signals are always void in Qt Jambi) offset += addString(metaData.metaData, strings, stringsInOrder, "", offset, metaDataOffset++); // Signal tag (Currently not supported by the moc either) offset += addString(metaData.metaData, strings, stringsInOrder, "", offset, metaDataOffset++); // Signal flags int flags = MethodSignal; int modifiers = signalField.getModifiers(); if ((modifiers & Modifier.PRIVATE) == Modifier.PRIVATE) flags |= MethodAccessPrivate; else if ((modifiers & Modifier.PROTECTED) == Modifier.PROTECTED) flags |= MethodAccessProtected; else if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC) flags |= MethodAccessPublic; metaData.metaData[metaDataOffset++] = flags; } // Slots (### make sure enum types are covered, ### also test QFlags) for (int i = 0; i < slots.size(); ++i) { Method slot = slots.get(i); String javaMethodSignature = methodSignature(slot); metaData.originalSignatures[signalFields.size() + i] = javaMethodSignature; // Slot signature offset += addString( metaData.metaData, strings, stringsInOrder, internalTypeNameOfMethodSignature(slot, 1), offset, metaDataOffset++); // Slot parameters offset += addString( metaData.metaData, strings, stringsInOrder, internalTypeNameOfParameters(slot, 1), offset, metaDataOffset++); // Slot type String returnType = slot.getReturnType().getName(); if (returnType.equals("void")) returnType = ""; offset += addString( metaData.metaData, strings, stringsInOrder, internalTypeNameOfClass(slot.getReturnType(), 0), offset, metaDataOffset++); // Slot tag (Currently not supported by the moc either) offset += addString(metaData.metaData, strings, stringsInOrder, "", offset, metaDataOffset++); // Slot flags int flags = MethodSlot; int modifiers = slot.getModifiers(); if ((modifiers & Modifier.PRIVATE) == Modifier.PRIVATE) flags |= MethodAccessPrivate; else if ((modifiers & Modifier.PROTECTED) == Modifier.PROTECTED) flags |= MethodAccessProtected; else if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC) flags |= MethodAccessPublic; metaData.metaData[metaDataOffset++] = flags; } metaData.slotsArray = slots.toArray(new Method[0]); String propertyNames[] = propertyReaders.keySet().toArray(new String[0]); metaData.propertyReadersArray = new Method[propertyNames.length]; metaData.propertyResettersArray = new Method[propertyNames.length]; metaData.propertyWritersArray = new Method[propertyNames.length]; metaData.propertyDesignablesArray = new Method[propertyNames.length]; for (int i = 0; i < propertyNames.length; ++i) { Method reader = propertyReaders.get(propertyNames[i]); Method writer = propertyWriters.get(propertyNames[i]); Method resetter = propertyResetters.get(propertyNames[i]); Object designableVariant = propertyDesignables.get(propertyNames[i]); boolean isUser = propertyUser.get(propertyNames[i]); if (writer != null && !reader.getReturnType().isAssignableFrom(writer.getParameterTypes()[0])) { System.err.println( "QtJambiInternal.buildMetaData: Writer for property " + propertyNames[i] + " takes a type which is incompatible with reader's return type."); writer = null; } // Name offset += addString( metaData.metaData, strings, stringsInOrder, propertyNames[i], offset, metaDataOffset++); // Type (need to special case flags and enums) Class<?> t = reader.getReturnType(); boolean isEnumOrFlags = Enum.class.isAssignableFrom(t) || QFlags.class.isAssignableFrom(t); String typeName = null; if (isEnumOrFlags && t.getDeclaringClass() != null && QObject.class.isAssignableFrom(t.getDeclaringClass())) { // To avoid using JObjectWrapper for enums and flags (which is required in certain cases.) typeName = t.getDeclaringClass().getName().replace(".", "::") + "::" + t.getSimpleName(); } else { typeName = internalTypeNameOfClass(t, 0); } offset += addString( metaData.metaData, strings, stringsInOrder, typeName, offset, metaDataOffset++); int designableFlags = 0; if (designableVariant instanceof Boolean) { if ((Boolean) designableVariant) designableFlags = PropertyDesignable; } else if (designableVariant instanceof Method) { designableFlags = PropertyResolveDesignable; metaData.propertyDesignablesArray[i] = (Method) designableVariant; } // Flags metaData.metaData[metaDataOffset++] = PropertyReadable | PropertyStored | designableFlags | (writer != null ? PropertyWritable : 0) | (resetter != null ? PropertyResettable : 0) | (isEnumOrFlags ? PropertyEnumOrFlag : 0) | (isUser ? PropertyUser : 0); metaData.propertyReadersArray[i] = reader; metaData.propertyWritersArray[i] = writer; metaData.propertyResettersArray[i] = resetter; } // Each property notifier takes 1 int // FIXME // Enum types int enumConstantOffset = metaDataOffset + enumCount * 4; Hashtable<String, Class<?>> enclosingClasses = new Hashtable<String, Class<?>>(); Collection<Class<?>> classes = enums.values(); for (Class<?> cls : classes) { Class<?> enclosingClass = cls.getEnclosingClass(); if (enclosingClass.equals(clazz)) { // Name offset += addString( metaData.metaData, strings, stringsInOrder, cls.getSimpleName(), offset, metaDataOffset++); // Flags (1 == flags, 0 == no flags) metaData.metaData[metaDataOffset++] = QFlags.class.isAssignableFrom(cls) ? 0x1 : 0x0; // Get the enum class Class<?> enumClass = Enum.class.isAssignableFrom(cls) ? cls : null; if (enumClass == null) { enumClass = getEnumForQFlags(cls); } enumConstantCount = enumClass.getEnumConstants().length; // Count metaData.metaData[metaDataOffset++] = enumConstantCount; // Data metaData.metaData[metaDataOffset++] = enumConstantOffset; enumConstantOffset += 2 * enumConstantCount; } else if (!enclosingClass.isAssignableFrom(clazz) && !enclosingClasses.contains(enclosingClass.getName())) { // If the enclosing class of an enum is not in the current class hierarchy, then // the generated meta object needs to have a pointer to its meta object in the // extra-data. enclosingClasses.put(enclosingClass.getName(), enclosingClass); } } metaData.extraDataArray = enclosingClasses.values().toArray(new Class<?>[0]); // Enum constants for (Class<?> cls : classes) { if (cls.getEnclosingClass().equals(clazz)) { // Get the enum class Class<?> enumClass = Enum.class.isAssignableFrom(cls) ? cls : null; if (enumClass == null) { enumClass = getEnumForQFlags(cls); } Enum<?> enumConstants[] = (Enum[]) enumClass.getEnumConstants(); for (Enum<?> enumConstant : enumConstants) { // Key offset += addString( metaData.metaData, strings, stringsInOrder, enumConstant.name(), offset, metaDataOffset++); // Value metaData.metaData[metaDataOffset++] = enumConstant instanceof QtEnumerator ? ((QtEnumerator) enumConstant).value() : enumConstant.ordinal(); } } } // EOD metaData.metaData[metaDataOffset++] = 0; int stringDataOffset = 0; metaData.stringData = new byte[offset + 1]; for (String s : stringsInOrder) { assert stringDataOffset == strings.get(s); System.arraycopy(s.getBytes(), 0, metaData.stringData, stringDataOffset, s.length()); stringDataOffset += s.length(); metaData.stringData[stringDataOffset++] = 0; } } return metaData; }