/*
* Copyright 2008-2017 the original author or authors.
*
* 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.codehaus.griffon.compile.core.ast.type;
import griffon.metadata.TypeProviderFor;
import org.codehaus.griffon.compile.core.processor.type.Type;
import org.codehaus.griffon.compile.core.processor.type.TypeCollector;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;
import org.kordamp.gipsy.transform.GipsyASTTransformation;
import org.kordamp.jipsy.ServiceProviderFor;
import org.kordamp.jipsy.processor.CheckResult;
import org.kordamp.jipsy.processor.LogLocation;
import org.kordamp.jipsy.processor.Persistence;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
/**
* @author Andres Almiray
* @since 2.1.0
*/
@ServiceProviderFor(ASTTransformation.class)
@GroovyASTTransformation(phase = CompilePhase.CLASS_GENERATION)
public class TypeProviderASTTransformation extends GipsyASTTransformation {
public static final String NAME = TypeProviderASTTransformation.class.getName()
+ " (" + TypeProviderASTTransformation.class.getPackage().getImplementationVersion() + ")";
private static final ClassNode SERVICE_PROVIDER_FOR_TYPE = makeClassSafe(TypeProviderFor.class);
private Persistence persistence;
private TypeCollector data;
@Override
protected ClassNode getAnnotationClassNode() {
return SERVICE_PROVIDER_FOR_TYPE;
}
@Override
protected void initialize(ModuleNode moduleNode) {
super.initialize(moduleNode);
File outputDir = moduleNode.getContext().getConfiguration().getTargetDirectory();
persistence = new TypePersistence(NAME, options.dir(), outputDir, logger);
data = new TypeCollector(persistence.getInitializer(), logger);
// Initialize if possible
for (String typeName : persistence.tryFind()) {
data.getType(typeName);
}
//data.cache();
}
@Override
protected void removeStaleData(ClassNode classNode, ModuleNode moduleNode) {
data.removeProvider(classNode.getName());
}
protected void handleAnnotations(ClassNode classNode, List<AnnotationNode> annotations, ModuleNode moduleNode) {
CheckResult checkResult = checkCurrentClass(classNode);
if (checkResult.isError()) {
addError(checkResult.getMessage(), classNode, moduleNode.getContext());
return;
}
for (ClassNode type : findTypes(annotations)) {
CheckResult implementationResult = isImplementation(classNode, type);
if (implementationResult.isError()) {
addError(implementationResult.getMessage(), classNode, moduleNode.getContext());
} else {
register(type.getName(), classNode);
}
}
}
@Override
protected void writeData() {
// if (data.isModified()) {
if (data.types().isEmpty()) {
logger.note(LogLocation.LOG_FILE, "Writing output");
try {
persistence.delete();
} catch (IOException e) {
logger.warning(LogLocation.LOG_FILE, "An error occurred while deleting data file");
}
} else {
logger.note(LogLocation.LOG_FILE, "Writing output");
for (Type type : data.types()) {
try {
persistence.write(type.getName(), type.toProviderNamesList());
} catch (IOException e) {
// TODO print out error
}
}
persistence.writeLog();
}
// }
}
private CheckResult checkCurrentClass(ClassNode currentClass) {
if (currentClass.isEnum()) {
return CheckResult.valueOf("is not a class nor an interface");
}
if (!isPublic(currentClass.getModifiers())) {
return CheckResult.valueOf("is not public");
}
if (isStatic(currentClass.getModifiers())) {
return CheckResult.valueOf("is static");
}
return CheckResult.OK;
}
private List<ClassNode> findTypes(List<AnnotationNode> annotations) {
List<ClassNode> types = new ArrayList<>();
for (AnnotationNode annotation : annotations) {
for (Expression expr : findCollectionValueMember(annotation, "value")) {
if (expr instanceof ClassExpression) {
types.add(((ClassExpression) expr).getType());
}
}
}
return types;
}
private void register(String typeName, ClassNode provider) {
data.getType(typeName).addProvider(provider.getName());
}
}