/* * 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.transform; import griffon.core.GriffonApplication; import griffon.metadata.ArtifactProviderFor; import org.codehaus.griffon.compile.core.BaseConstants; import org.codehaus.griffon.compile.core.ast.SourceUnitCollector; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassHelper; 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.SourceUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.inject.Inject; import java.util.List; import static java.util.Objects.requireNonNull; import static org.codehaus.griffon.compile.core.ast.GriffonASTUtils.injectInterface; /** * Handles generation of code for Griffon artifacts. * <p> * * @author Andres Almiray * @since 2.0.0 */ public abstract class GriffonArtifactASTTransformation extends AbstractASTTransformation implements BaseConstants { protected static final String ERROR_CLASS_NODE_NULL = "Argument 'classNode' must not be null"; protected static final String ERROR_SOURCE_NULL = "Argument 'source' must not be null"; private static final Logger LOG = LoggerFactory.getLogger(GriffonArtifactASTTransformation.class); private static final ClassNode GRIFFON_APPLICATION_TYPE = makeClassSafe(GriffonApplication.class); private static final ClassNode INJECT_TYPE = makeClassSafe(Inject.class); public static boolean isOrImplements(ClassNode fieldType, ClassNode interfaceType) { return fieldType.equals(interfaceType) || fieldType.implementsInterface(interfaceType); } protected static boolean isArtifact(@Nonnull ClassNode classNode, @Nonnull SourceUnit source, @Nonnull ClassNode artifactType) { requireNonNull(classNode, ERROR_CLASS_NODE_NULL); requireNonNull(source, ERROR_SOURCE_NULL); List<AnnotationNode> annotations = classNode.getAnnotations(makeClassSafe(ArtifactProviderFor.class)); if (annotations == null || annotations.isEmpty() || annotations.size() != 1) { return false; } AnnotationNode artifact = annotations.get(0); Expression value = artifact.getMember("value"); return value instanceof ClassExpression && value.getType().equals(artifactType); } public void visit(ASTNode[] nodes, SourceUnit source) { ModuleNode moduleNode = (ModuleNode) nodes[0]; ClassNode classNode = moduleNode.getClasses().get(0); if (classNode.isDerivedFrom(ClassHelper.SCRIPT_TYPE) || !matches(classNode, source)) { return; } transform(classNode); } protected void transform(ClassNode classNode) { ClassNode superClass = classNode.getSuperClass(); ClassNode superClassNode = getSuperClassNode(classNode); if (superClassNode != null && ClassHelper.OBJECT_TYPE.equals(superClass)) { LOG.debug("Setting {} as the superclass of {}", superClassNode.getName(), classNode.getName()); classNode.setSuperClass(superClassNode); } else if (!classNode.implementsInterface(getInterfaceNode())) { inject(classNode, superClass); } } protected void inject(ClassNode classNode, ClassNode superClass) { SourceUnit superSource = SourceUnitCollector.getInstance().getSourceUnit(superClass); if (superSource == null) { ClassNode interfaceNode = getInterfaceNode(); LOG.debug("Injecting {} behavior to {}", interfaceNode.getName(), classNode.getName()); // 1. add interface injectInterface(classNode, interfaceNode); // 2. add methods for (ASTInjector injector : getASTInjectors()) { injector.inject(classNode, getArtifactType()); } postInject(classNode); } else if (!matches(superClass, superSource)) { transform(superClass); } } protected abstract String getArtifactType(); protected ClassNode getSuperClassNode(ClassNode classNode) { return null; } protected abstract ClassNode getInterfaceNode(); protected abstract boolean matches(ClassNode classNode, SourceUnit source); protected abstract ASTInjector[] getASTInjectors(); protected void postInject(ClassNode classNode) { } }