/** * Uses reflection to extract options based on the fields of the provided config class * ("List extensions" field is ignored, pending removal, Char is turned into String) * The class must be annotated with {@code @JsonTypeName("type name")} * @param pluginConfigClass the config class we want to extract options from through reflection */ FormatPluginOptionsDescriptor(Class<? extends FormatPluginConfig> pluginConfigClass) { this.pluginConfigClass = pluginConfigClass; Map<String, TableParamDef> paramsByName = new LinkedHashMap<>(); Field[] fields = pluginConfigClass.getDeclaredFields(); // @JsonTypeName("text") JsonTypeName annotation = pluginConfigClass.getAnnotation(JsonTypeName.class); this.typeName = annotation != null ? annotation.value() : null; if (this.typeName != null) { paramsByName.put("type", new TableParamDef("type", String.class)); } for (Field field : fields) { if (Modifier.isStatic(field.getModifiers()) // we want to deprecate this field || (field.getName().equals("extensions") && field.getType() == List.class)) { continue; } Class<?> fieldType = field.getType(); if (fieldType == char.class) { // calcite does not like char type. Just use String and enforce later that length == 1 fieldType = String.class; } paramsByName.put(field.getName(), new TableParamDef(field.getName(), fieldType).optional()); } this.functionParamsByName = unmodifiableMap(paramsByName); }
public void assignTypeNameIfNecessary(ClassesMapping classesMapping, ClassMappingInternal<Object> currentClassMapping) { if (!currentClassMapping.hasAnnotation(JsonTypeName.class)) { typesWithPolymorphism.stream() .filter(typeWithPolymorphism -> typeWithPolymorphism.isAssignableFrom(currentClassMapping.getType())) .findFirst() .ifPresent(typeWithPolymorphism -> classesMapping.getOpt(typeWithPolymorphism) .ifPresent(classMapping -> classMapping.getAnnotationOpt(JsonSubTypes.class) .ifPresent((jsonSubTypes) -> Arrays.asList(jsonSubTypes.value()).stream() .filter(subtype -> subtype.value().equals(classMapping.getType())) .findFirst() .map(JsonSubTypes.Type::name) .ifPresent(classMapping::typeName) ) ) ); } }
protected void testRoot() { // while boundaries are compiler-checked, let's still verify superclass, as generics in Java are easy to bypass assertTrue("Invalid root type: " + expectedRoot, PolymorphicConfiguration.class.isAssignableFrom(expectedRoot)); JsonTypeInfo typeInfo = expectedRoot.getAnnotation(JsonTypeInfo.class); // TODO: test "property" and "use" values of the annotation assertNotNull("Root is not annotated with @JsonTypeInfo", typeInfo); if (expectedDefault != null) { assertTrue("Default type is not specified on root. Expected: " + expectedDefault.getName(), hasDefault(typeInfo)); assertEquals("Expected and actual default types are not the same", expectedDefault, typeInfo.defaultImpl()); } else { assertFalse("Expected no default type, but @JsonTypeInfo sets it to " + typeInfo.defaultImpl().getName() + ".", hasDefault(typeInfo)); } if (isConcrete(expectedRoot)) { JsonTypeName typeName = expectedRoot.getAnnotation(JsonTypeName.class); assertNotNull("Concrete root configuration type must be annotated with @JsonTypeName: " + expectedRoot.getName()); } }
public void init(JavaType baseType) { this.baseType = baseType; ArrayList<Class<?>> classes = new ArrayList<Class<?>>(); classes.add(baseType.getRawClass()); classes.addAll(Arrays.asList(DtoModules.extensionClasses())); for ( Class<?> c : classes) { if( baseType.getRawClass().isAssignableFrom(c) ) { JsonTypeName jsonAnnoation = c.getAnnotation(JsonTypeName.class); if(jsonAnnoation!=null && jsonAnnoation.value()!=null) { typeToId.put(c, jsonAnnoation.value()); idToType.put(jsonAnnoation.value(), TypeFactory.defaultInstance().constructSpecializedType(baseType, c)); idToType.put(c.getName(), TypeFactory.defaultInstance().constructSpecializedType(baseType, c)); } else { XmlRootElement xmlAnnoation = c.getAnnotation(XmlRootElement.class); if(xmlAnnoation!=null && xmlAnnoation.name()!=null) { typeToId.put(c, xmlAnnoation.name()); idToType.put(xmlAnnoation.name(), TypeFactory.defaultInstance().constructSpecializedType(baseType, c)); idToType.put(c.getName(), TypeFactory.defaultInstance().constructSpecializedType(baseType, c)); } } } } }
@Override public <J> Runnable runnerFor(J job) { Assert.notNull(job, "Pre-condition violated: job != null."); JsonTypeName type = job.getClass().getAnnotation(JsonTypeName.class); Assert.notNull(type, "Pre-condition violated: Job has a @JsonTypeName."); String jsonType = type.value(); if (!StringUtils.hasLength(jsonType)) { // If the type is not specified in the annotation, use the simple class name as documented. jsonType = job.getClass().getSimpleName(); } // Check for bean existence and that bean is a prototype. // Beans have to be prototypes, because they are configured with the job vars below. if (!applicationContext.isPrototype(jsonType)) { throw new IllegalArgumentException(String.format( "Job runners have to be prototypes, but job runner %s is none.", jsonType)); } Object runner = applicationContext.getBean(jsonType, job); if (!(runner instanceof Runnable)) { throw new IllegalArgumentException(String.format( "Job runners need to be Runnable, but job runner %s (%s) is not.", jsonType, runner.getClass().getName())); } return (Runnable) runner; }
@Test public void test() { DrillConfig config = DrillConfig.create(); ScanResult scanResult = RunTimeScan.fromPrescan(config); FormatPluginOptionExtractor e = new FormatPluginOptionExtractor(scanResult); Collection<FormatPluginOptionsDescriptor> options = e.getOptions(); for (FormatPluginOptionsDescriptor d : options) { assertEquals(d.pluginConfigClass.getAnnotation(JsonTypeName.class).value(), d.typeName); switch (d.typeName) { case "text": assertEquals(TextFormatConfig.class, d.pluginConfigClass); assertEquals( "(type: String, lineDelimiter: String, fieldDelimiter: String, quote: String, escape: String, comment: String, skipFirstLine: boolean, extractHeader: boolean)", d.presentParams() ); break; case "named": assertEquals(NamedFormatPluginConfig.class, d.pluginConfigClass); assertEquals("(type: String, name: String)", d.presentParams()); break; case "parquet": assertEquals(d.typeName, "(type: String, autoCorrectCorruptDates: boolean)", d.presentParams()); break; case "json": case "sequencefile": case "pcap": case "avro": assertEquals(d.typeName, "(type: String)", d.presentParams()); break; case "httpd": assertEquals("(type: String, logFormat: String, timestampFormat: String)", d.presentParams()); break; default: fail("add validation for format plugin type " + d.typeName); } } }
/** * Returns the identifier of a given class * * @param c * the class whose identifier is requested * @return the class identifier */ public static String getStructureElementIdentifier( Class<? extends StructureElement> c) { String structureElementAbbreviation; if (c.isAnnotationPresent(JsonTypeName.class)) { JsonTypeName info = c.getAnnotation(JsonTypeName.class); structureElementAbbreviation = info.value(); } else { structureElementAbbreviation = c.getSimpleName().toUpperCase(); } return structureElementAbbreviation; }
protected void testNonRoot(Class<? extends T> t) { // while boundaries are compiler-checked, let's still verify superclass, as generics in Java are easy to bypass assertTrue("Invalid type " + t.getName() + ". Must be a subclass of root type " + expectedRoot.getName(), expectedRoot.isAssignableFrom(t)); assertTrue("Non-root configuration type must not be abstract: " + t.getName(), isConcrete(t)); // this check would prevent matching subclasses by class, but we discourage that anyways.. (otherwise FQN // would have to be used in YAML) JsonTypeName typeName = t.getAnnotation(JsonTypeName.class); assertNotNull("Non-root configuration type must be annotated with @JsonTypeName: " + t.getName()); }
protected String extractTypeLabel(Class<?> type) { // TODO: get rid of Jackson annotations dependency .. devise our own that reflect Bootique style of config factory // subclassing... JsonTypeName typeName = type.getAnnotation(JsonTypeName.class); return typeName != null ? typeName.value() : null; }
private <T extends Filter> String buildTypeId(final String id, final Class<T> type) { final JsonTypeName annotation = type.getAnnotation(JsonTypeName.class); if (annotation == null) { return id; } return annotation.value(); }
private String getTypeLabel(Class<? extends ManagedDataSourceFactory> factoryType) { // TODO: see TODO in ConfigMetadataCompiler ... at least maybe create a public API for this in Bootique to // avoid parsing annotations inside the modules... JsonTypeName typeName = factoryType.getAnnotation(JsonTypeName.class); if (typeName == null) { throw new BootiqueException(1, "Invalid ManagedDataSourceFactory: " + factoryType.getName() + ". Not annotated with @JsonTypeName."); } return typeName.value(); }
public String findTypeName(AnnotatedClass paramAnnotatedClass) { JsonTypeName localJsonTypeName = (JsonTypeName)paramAnnotatedClass.getAnnotation(JsonTypeName.class); if (localJsonTypeName == null) return null; return localJsonTypeName.value(); }
private String getTypeName(JsonTypeInfo parentJsonTypeInfo, final Class<?> cls) { // Id.CLASS if (parentJsonTypeInfo.use() == JsonTypeInfo.Id.CLASS) { return cls.getName(); } // find @JsonTypeName recursively final JsonTypeName jsonTypeName = getAnnotationRecursive(cls, JsonTypeName.class); if (jsonTypeName != null && !jsonTypeName.value().isEmpty()) { return jsonTypeName.value(); } // find @JsonSubTypes.Type recursively final JsonSubTypes jsonSubTypes = getAnnotationRecursive(cls, JsonSubTypes.class, new Predicate<JsonSubTypes>() { @Override public boolean test(JsonSubTypes types) { return getJsonSubTypeForClass(types, cls) != null; } }); if (jsonSubTypes != null) { final JsonSubTypes.Type jsonSubType = getJsonSubTypeForClass(jsonSubTypes, cls); if (!jsonSubType.name().isEmpty()) { return jsonSubType.name(); } } // use simplified class name if it's not an interface or abstract if(!cls.isInterface() && !Modifier.isAbstract(cls.getModifiers())) { return cls.getName().substring(cls.getName().lastIndexOf(".") + 1); } return null; }
@Override public void serialize(Wrapper value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeStartObject(); String name = value.getElement().getClass().getAnnotation(JsonTypeName.class).value(); jgen.writeFieldName(name); provider.defaultSerializeValue(value.getElement(), jgen); jgen.writeEndObject(); }
private static String extractTypeMetadata( TreeLogger logger, RebindConfiguration configuration, JClassType baseType, JClassType subtype, JsonTypeInfo typeInfo, Optional<JsonSubTypes> propertySubTypes, Optional<JsonSubTypes> baseSubTypes, ImmutableList<JClassType> allSubtypes ) throws UnableToCompleteException { switch ( typeInfo.use() ) { case NAME: // we first look the name on JsonSubTypes annotations. Top ones override the bottom ones. String name = findNameOnJsonSubTypes( baseType, subtype, allSubtypes, propertySubTypes, baseSubTypes ); if ( null != name && !"".equals( name ) ) { return name; } // we look if the name is defined on the type with JsonTypeName Optional<JsonTypeName> typeName = findFirstEncounteredAnnotationsOnAllHierarchy( configuration, subtype, JsonTypeName .class ); if ( typeName.isPresent() && !Strings.isNullOrEmpty( typeName.get().value() ) ) { return typeName.get().value(); } // we use the default name (ie simple name of the class) String simpleBinaryName = subtype.getQualifiedBinaryName(); int indexLastDot = simpleBinaryName.lastIndexOf( '.' ); if ( indexLastDot != -1 ) { simpleBinaryName = simpleBinaryName.substring( indexLastDot + 1 ); } return simpleBinaryName; case MINIMAL_CLASS: if ( !baseType.getPackage().isDefault() ) { String basePackage = baseType.getPackage().getName(); if ( subtype.getQualifiedBinaryName().startsWith( basePackage + "." ) ) { return subtype.getQualifiedBinaryName().substring( basePackage.length() ); } } case CLASS: return subtype.getQualifiedBinaryName(); default: logger.log( TreeLogger.Type.ERROR, "JsonTypeInfo.Id." + typeInfo.use() + " is not supported" ); throw new UnableToCompleteException(); } }
@Nonnull @Override public Result<String> alias(TypeDescriptor type) { JsonTypeName typeName = type.getAnnotation(JsonTypeName.class); if (typeName != null && !isNullOrEmpty(typeName.value())) { return Result.of(typeName.value()); } return Result.notFound(); }
public String getName(JavaType type) { Class<?> rawClass = type.getRawClass(); JsonTypeName typeName = rawClass.getAnnotation(JsonTypeName.class); if (typeName != null) { return typeName.value(); } else { return getName(rawClass); } }
@Test public void test() { FormatPluginOptionExtractor e = new FormatPluginOptionExtractor(CLASSPATH_SCAN_RESULT); Collection<FormatPluginOptionsDescriptor> options = e.getOptions(); for (FormatPluginOptionsDescriptor d : options) { assertEquals(d.pluginConfigClass.getAnnotation(JsonTypeName.class).value(), d.typeName); switch (d.typeName) { case "text": assertEquals(TextFormatConfig.class, d.pluginConfigClass); assertEquals( "(type: String, lineDelimiter: String, fieldDelimiter: String, quote: String, escape: String, " + "comment: String, skipFirstLine: boolean, extractHeader: boolean, " + "autoGenerateColumnNames: boolean, trimHeader: boolean, outputExtension: String)", d.presentParams() ); break; case "named": assertEquals(NamedFormatPluginConfig.class, d.pluginConfigClass); assertEquals("(type: String, name: String)", d.presentParams()); break; case "json": assertEquals(d.typeName, "(type: String, outputExtension: String, prettyPrint: boolean)", d.presentParams()); break; case "parquet": assertEquals(d.typeName, "(type: String, autoCorrectCorruptDates: boolean, outputExtension: String)", d.presentParams()); break; case "arrow": assertEquals(d.typeName, "(type: String, outputExtension: String)", d.presentParams()); break; case "sequencefile": case "avro": assertEquals(d.typeName, "(type: String)", d.presentParams()); break; case "excel": assertEquals(d.typeName, "(type: String, sheet: String, extractHeader: boolean, hasMergedCells: boolean, xls: boolean)", d.presentParams()); break; default: fail("add validation for format plugin type " + d.typeName); } } }
/** * Constructor. */ public TypeScanner() { super(JsonTypeName.class); }
@Override public Class<? extends Annotation> annotationType() { return JsonTypeName.class; }
@Override public JsonTypeName build() { return jsonTypeName; }
private void getInheritance(ClassInformation information, ReflectClass<?> cls) { JsonTypeName typeName = cls.getAnnotation(JsonTypeName.class); if (information.typeName == null) { information.typeName = ""; } if (typeName != null) { information.typeName = typeName.value(); } if (information.typeName.isEmpty()) { information.typeName = getUnqualifiedName(cls.getName()); } JsonTypeInfo typeInfo = cls.getAnnotation(JsonTypeInfo.class); if (typeInfo != null) { String defaultProperty = ""; switch (typeInfo.use()) { case CLASS: information.inheritance.value = InheritanceValue.CLASS; defaultProperty = "@class"; break; case MINIMAL_CLASS: information.inheritance.value = InheritanceValue.MINIMAL_CLASS; defaultProperty = "@c"; break; case NAME: information.inheritance.value = InheritanceValue.NAME; defaultProperty = "@type"; break; case NONE: information.inheritance.value = InheritanceValue.NONE; break; default: diagnostics.warning(null, "{{t0}}: unsupported value " + typeInfo.use() + " in {{t1}}", cls, JsonTypeInfo.Id.class); break; } if (information.inheritance.value != InheritanceValue.NONE) { switch (typeInfo.include()) { case PROPERTY: information.inheritance.key = InheritanceKey.PROPERTY; break; case WRAPPER_ARRAY: information.inheritance.key = InheritanceKey.WRAPPER_ARRAY; break; case WRAPPER_OBJECT: information.inheritance.key = InheritanceKey.WRAPPER_OBJECT; break; default: diagnostics.warning(null, "{{t0}}: unsupported value " + typeInfo.include() + " in {{t1}}", cls, JsonTypeInfo.As.class); break; } } if (information.inheritance.key == InheritanceKey.PROPERTY) { String property = typeInfo.property(); if (property.isEmpty()) { property = defaultProperty; } information.inheritance.propertyName = property; } } }
private void registerSubtypes(Reflections reflections,ObjectMapper objectMapper) { Set<Class<?>> subTypes = reflections.getTypesAnnotatedWith(JsonTypeName.class); objectMapper.registerSubtypes(subTypes.toArray(new Class<?>[subTypes.size()])); }