/**
* Copyright (C) 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (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
*
* 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.jboss.errai.enterprise.rebind;
import static org.jboss.errai.codegen.builder.impl.ObjectBuilder.newInstanceOf;
import static org.jboss.errai.codegen.util.Stmt.castTo;
import static org.jboss.errai.codegen.util.Stmt.invokeStatic;
import static org.jboss.errai.codegen.util.Stmt.loadVariable;
import static org.jboss.errai.enterprise.client.cdi.EventQualifierSerializer.SERIALIZER_CLASS_NAME;
import static org.jboss.errai.enterprise.client.cdi.EventQualifierSerializer.SERIALIZER_PACKAGE_NAME;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Function;
import org.jboss.errai.codegen.Parameter;
import org.jboss.errai.codegen.builder.ClassStructureBuilder;
import org.jboss.errai.codegen.builder.ConstructorBlockBuilder;
import org.jboss.errai.codegen.builder.ContextualStatementBuilder;
import org.jboss.errai.codegen.builder.impl.ClassBuilder;
import org.jboss.errai.codegen.builder.impl.ObjectBuilder;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.util.CDIAnnotationUtils;
import org.jboss.errai.codegen.util.ClassChangeUtil;
import org.jboss.errai.common.client.api.Assert;
import org.jboss.errai.common.client.util.AnnotationPropertyAccessorBuilder;
import org.jboss.errai.common.client.util.SharedAnnotationSerializer;
import org.jboss.errai.enterprise.client.cdi.EventQualifierSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Max Barkley <mbarkley@redhat.com>
*/
public class NonGwtEventQualifierSerializerGenerator {
private static final Logger logger = LoggerFactory.getLogger(NonGwtEventQualifierSerializerGenerator.class);
private NonGwtEventQualifierSerializerGenerator() {}
@SuppressWarnings("unchecked")
public static Class<? extends EventQualifierSerializer> generateAndLoad() {
logger.info("Generating source for {}.{}...", SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME);
final String source = generateSource(CDIAnnotationUtils.getQualifiers());
logger.info("Successfully generated source for {}.{}", SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME);
logger.info("Attempting to compile and load {}.{}", SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME);
return (Class<? extends EventQualifierSerializer>) ClassChangeUtil
.compileAndLoadFromSource(SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME, source);
}
static String generateSource(final Iterable<MetaClass> qualifiers) {
final ClassStructureBuilder<?> body = ClassBuilder
.define(SERIALIZER_PACKAGE_NAME + "." + SERIALIZER_CLASS_NAME, EventQualifierSerializer.class)
.publicScope().body();
final ConstructorBlockBuilder<?> ctor = body.publicConstructor();
for (final MetaClass qual : qualifiers) {
final Collection<MetaMethod> bindingAttributes = CDIAnnotationUtils.getAnnotationAttributes(qual);
if (!bindingAttributes.isEmpty()) {
ctor.append(loadVariable("serializers").invoke("put", qual.getFullyQualifiedName(), generateEntryStatement(qual, bindingAttributes)));
}
}
ctor.finish();
return body.toJavaString();
}
private static ContextualStatementBuilder generateEntryStatement(final MetaClass qual,
final Collection<MetaMethod> bindingAttributes) {
ContextualStatementBuilder entryStmt = invokeStatic(AnnotationPropertyAccessorBuilder.class, "create");
for (final MetaMethod attr : bindingAttributes) {
entryStmt = entryStmt.invoke("with", attr.getName(), anonymousAttributeAccessorFor(attr));
}
entryStmt = entryStmt.invoke("build");
return entryStmt;
}
private static ObjectBuilder anonymousAttributeAccessorFor(final MetaMethod attr) {
return newInstanceOf(Function.class).extend()
.publicOverridesMethod("apply", Parameter.finalOf(Object.class, "anno"))
.append(invokeStatic(SharedAnnotationSerializer.class, "stringify",
castTo(attr.getDeclaringClass(), loadVariable("anno")).invoke(attr))
.returnValue())
.finish().finish();
}
public static void loadAndSetEventQualifierSerializer() {
logger.info("Attempting to load {}.{}", SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME);
final Optional<Class<?>> loadedImpl = ClassChangeUtil.loadClassIfPresent(
SERIALIZER_PACKAGE_NAME,
SERIALIZER_CLASS_NAME);
if (loadedImpl.isPresent()) {
logger.info("Successfully loaded {}.{}", SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME);
final Class<?> clazz = loadedImpl.get();
instantiateAndSetEventQualifierSerializer(clazz);
}
else {
logger.warn("No {}.{} found on the classpath. Attempting to generate and load.",
SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME);
final Class<? extends EventQualifierSerializer> clazz;
try {
clazz = Assert.notNull(generateAndLoad());
} catch (final Throwable t) {
throw new RuntimeException("Could not generate " + EventQualifierSerializer.SERIALIZER_CLASS_NAME, t);
}
logger.info("Successfully generated and loaded {}.{}", SERIALIZER_PACKAGE_NAME, SERIALIZER_CLASS_NAME);
instantiateAndSetEventQualifierSerializer(clazz);
}
}
public static void instantiateAndSetEventQualifierSerializer(final Class<?> clazz) {
try {
EventQualifierSerializer.set(clazz.asSubclass(EventQualifierSerializer.class).newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Could not instantiate " + SERIALIZER_PACKAGE_NAME + "."
+ SERIALIZER_CLASS_NAME + " with default constructor.", e);
} catch (final ClassCastException e) {
throw new RuntimeException(SERIALIZER_PACKAGE_NAME + "." + SERIALIZER_CLASS_NAME
+ " must be a subclass of " + EventQualifierSerializer.class.getName(), e);
}
}
}